Compare commits

..

1 Commits

Author SHA1 Message Date
马广威
d187bb2bba Accept Merge Request #1211: (release/release_2.2 -> master)
Merge Request: SPRINT-MES-F-2024-04、05版本更新

Created By: @马广威
Accepted By: @马广威
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1211
2024-08-01 21:58:11 +08:00
92 changed files with 6809 additions and 3604 deletions

View File

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

View File

@@ -1,35 +0,0 @@
# -*- coding: utf-8 -*-
{
'name': "jikimo_account_process",
'summary': """
Short (1 phrase/line) summary of the module's purpose, used as
subtitle on modules listing or apps.openerp.com""",
'description': """
Long description of module's purpose
""",
'author': "My Company",
'website': "https://www.yourcompany.com",
# Categories can be used to filter modules in modules listing
# Check https://github.com/odoo/odoo/blob/16.0/odoo/addons/base/data/ir_module_category_data.xml
# for the full list
'category': 'Uncategorized',
'version': '0.1',
# any module necessary for this one to work correctly
'depends': ['base', 'account'],
# always loaded
'data': [
# 'security/ir.model.access.csv',
# 'views/views.xml',
# 'views/templates.xml',
],
# only loaded in demonstration mode
'demo': [
# 'demo/demo.xml',
],
}

View File

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

View File

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

View File

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

View File

@@ -1,15 +0,0 @@
from odoo import models, fields, api
from odoo.exceptions import ValidationError
class CustomAccountMoveLine(models.Model):
_inherit = 'account.move'
_description = "account move line"
@api.model_create_multi
def create(self, vals):
for val in vals:
val['name'] = self.env['ir.sequence'].next_by_code('account.move') or '/'
# 因为供应商与客户支付创建流程是先创建move line在修改来填充account_payment与move line的关联
return super(CustomAccountMoveLine, self).create(vals)

View File

@@ -1,18 +0,0 @@
# -*- coding: utf-8 -*-
# from odoo import models, fields, api
# class jikimo_account_process(models.Model):
# _name = 'jikimo_account_process.jikimo_account_process'
# _description = 'jikimo_account_process.jikimo_account_process'
# name = fields.Char()
# value = fields.Integer()
# value2 = fields.Float(compute="_value_pc", store=True)
# description = fields.Text()
#
# @api.depends('value')
# def _value_pc(self):
# for record in self:
# record.value2 = float(record.value) / 100

View File

@@ -53,23 +53,6 @@ const tableRequiredList = [
] ]
patch(FormStatusIndicator.prototype, 'jikimo_frontend.FormStatusIndicator', { patch(FormStatusIndicator.prototype, 'jikimo_frontend.FormStatusIndicator', {
setup() {
owl.onMounted(() => {
try {
const dom = this.__owl__.bdom.el
const buttonsDom = $(dom).find('.o_form_status_indicator_buttons ')
if (buttonsDom) {
const dom1 = buttonsDom.children('.o_form_button_save')
const dom2 = buttonsDom.children('.o_form_button_cancel')
dom1.append('保存')
dom2.append('取消')
}
} catch (e) {
console.log(e)
}
});
},
// 你可以重写或者添加一些方法和属性 // 你可以重写或者添加一些方法和属性
async _onDiscardChanges() { async _onDiscardChanges() {
// var self = this; // var self = this;
@@ -200,6 +183,17 @@ patch(ListRenderer.prototype, 'jikimo_frontend.ListRenderer', {
// }) // })
$(function () { $(function () {
document.addEventListener('click', function () {
const dom = $('.o_form_status_indicator_buttons ')
if (dom) {
const dom1 = dom.children().eq(0)
const dom2 = dom.children().eq(1)
if (!dom1.text()) {
dom1.append('保存')
dom2.append('取消')
}
}
})
function customRequired() { function customRequired() {
let timer = null let timer = null

View File

@@ -324,4 +324,4 @@ def unlink(self):
BaseModel._create = _create BaseModel._create = _create
# BaseModel.unlink = unlink BaseModel.unlink = unlink

View File

@@ -35,7 +35,6 @@
], ],
'web.assets_backend': [ 'web.assets_backend': [
'sf_base/static/src/scss/*.scss', 'sf_base/static/src/scss/*.scss',
'sf_base/static/src/js/*.js',
], ],
}, },

View File

@@ -5,4 +5,3 @@ from . import fixture
from . import functional_fixture from . import functional_fixture
from . import tool_other_features from . import tool_other_features
from . import basic_parameters_fixture from . import basic_parameters_fixture
from . import ir_sequence

View File

@@ -13,7 +13,7 @@ class BasicParametersFixture(models.Model):
diameter = fields.Float('直径(mm)', digits=(16, 2)) diameter = fields.Float('直径(mm)', digits=(16, 2))
# '零点卡盘' 字段 # '零点卡盘' 字段
weight = fields.Float('重量(kg)', digits=(16, 2)) weight = fields.Float('重量(mm)', digits=(16, 2))
orientation_dish_diameter = fields.Float('定位盘直径(mm)', digits=(16, 2)) orientation_dish_diameter = fields.Float('定位盘直径(mm)', digits=(16, 2))
clamping_diameter = fields.Float('装夹直径(mm)', digits=(16, 2)) clamping_diameter = fields.Float('装夹直径(mm)', digits=(16, 2))
clamping_num = fields.Selection([('1', '1'), ('2', '2'), ('4', '4'), ('6', '6'), ('8', '8')], string='装夹单元数') clamping_num = fields.Selection([('1', '1'), ('2', '2'), ('4', '4'), ('6', '6'), ('8', '8')], string='装夹单元数')

View File

@@ -84,12 +84,10 @@ class MrsProductionProcessCategory(models.Model):
class MrsProductionProcess(models.Model): class MrsProductionProcess(models.Model):
_name = 'sf.production.process' _name = 'sf.production.process'
_description = '表面工艺' _description = '表面工艺'
order = 'sequence asc'
code = fields.Char("编码") code = fields.Char("编码")
name = fields.Char('名称') name = fields.Char('名称')
remark = fields.Text("备注") remark = fields.Text("备注")
sequence = fields.Integer('排序')
# processing_order_ids = fields.One2many('sf.processing.order', 'production_process_id', string='工序') # processing_order_ids = fields.One2many('sf.processing.order', 'production_process_id', string='工序')
partner_process_ids = fields.Many2many('res.partner', 'process_ids', '加工工厂') partner_process_ids = fields.Many2many('res.partner', 'process_ids', '加工工厂')
active = fields.Boolean('有效', default=True) active = fields.Boolean('有效', default=True)
@@ -98,7 +96,7 @@ class MrsProductionProcess(models.Model):
# workcenter_ids = fields.Many2many('mrp.workcenter', 'rel_workcenter_process', required=True) # workcenter_ids = fields.Many2many('mrp.workcenter', 'rel_workcenter_process', required=True)
processing_day = fields.Float('加工天数/d') processing_day = fields.Float('加工天数/d')
travel_day = fields.Float('路途天数/d') travel_day = fields.Float('路途天数/d')
sequence = fields.Integer('排序')
# class MrsProcessingTechnology(models.Model): # class MrsProcessingTechnology(models.Model):
# _name = 'sf.processing.technology' # _name = 'sf.processing.technology'
@@ -150,7 +148,6 @@ class MrsProductionProcessParameter(models.Model):
processing_day = fields.Float('加工天数/d') processing_day = fields.Float('加工天数/d')
travel_day = fields.Float('路途天数/d') travel_day = fields.Float('路途天数/d')
active = fields.Boolean('有效', default=True) active = fields.Boolean('有效', default=True)
processing_mm = fields.Char('加工厚度/mm')
def name_get(self): def name_get(self):
result = [] result = []

View File

@@ -1,74 +0,0 @@
import calendar
from datetime import timedelta
from odoo import models, fields
class IrSequence(models.Model):
_inherit = 'ir.sequence'
date_range_period = fields.Selection(
[('day', '每日'), ('month', '每月'), ('year', '每年')],
string='日期期间',
)
def _next(self, sequence_date=None):
""" Returns the next number in the preferred sequence in all the ones given in self."""
if not self.use_date_range:
return self._next_do()
# date mode
dt = sequence_date or self._context.get('ir_sequence_date', fields.Date.today())
seq_date = self.env['ir.sequence.date_range'].search(
[
('sequence_id', '=', self.id),
('date_from', '<=', dt),
('date_to', '>=', dt),
('date_range_period', '=', self.date_range_period)
], limit=1)
if not seq_date:
if self.date_range_period:
seq_date = self._create_date_range_seq_by_period(dt, self.date_range_period)
else:
seq_date = self._create_date_range_seq(dt)
return seq_date.with_context(ir_sequence_date_range=seq_date.date_from)._next()
def _create_date_range_seq_by_period(self, date, period):
if period == 'year':
year = fields.Date.from_string(date).strftime('%Y')
date_from = '{}-01-01'.format(year)
date_to = '{}-12-31'.format(year)
if period == 'month':
# 计算当前月份的第一天和最后一天
year = fields.Date.from_string(date).strftime('%Y')
month = fields.Date.from_string(date).strftime('%m')
date_from = fields.Date.from_string(date).strftime('%Y-%m-01')
date_to = '{}-{}-{}'.format(year, month, calendar.monthrange(int(year), int(month))[1])
if period == 'day':
date_from = date
date_to = date
date_range = self.env['ir.sequence.date_range'].search(
[
('sequence_id', '=', self.id),
('date_to', '>=', date_from),
('date_to', '<=', date),
('date_range_period', '=', period)
],
order='date_to desc', limit=1)
if date_range:
date_from = date_range.date_to + timedelta(days=1)
seq_date_range = self.env['ir.sequence.date_range'].sudo().create({
'date_from': date_from,
'date_to': date_to,
'sequence_id': self.id,
'date_range_period': period,
})
return seq_date_range
class IrSequenceDateRange(models.Model):
_inherit = 'ir.sequence.date_range'
date_range_period = fields.Selection(
[('day', '每日'), ('month', '每月'), ('year', '每年')],
string='日期期间',
)

View File

@@ -0,0 +1,10 @@
diff a/sf_base/models/tool_base_new.py b/sf_base/models/tool_base_new.py (rejected hunks)
@@ -108,6 +108,4 @@
cutting_speed_ids = fields.One2many('sf.cutting.speed', 'standard_library_id', string='切削速度Vc')
- feed_per_tooth_ids = fields.One2many('sf.feed.per.tooth', 'standard_library_id', '每齿走刀量fz',
- domain=[('cutting_speed', '!=', False)])
- feed_per_tooth_ids_3 = fields.One2many('sf.feed.per.tooth', 'standard_library_id', '每齿走刀量fz',
- domain=[('cutting_speed', '!=', False)])
+ feed_per_tooth_ids = fields.One2many('sf.feed.per.tooth', 'standard_library_id', '每齿走刀量fz')
+ feed_per_tooth_ids_3 = fields.One2many('sf.feed.per.tooth', 'standard_library_id', '每齿走刀量fz')

View File

@@ -1,23 +0,0 @@
/** @odoo-module **/
import { registry } from '@web/core/registry';
import { formView } from '@web/views/form/form_view';
import { FormController } from '@web/views/form/form_controller';
import { onRendered, onMounted } from "@odoo/owl";
export class RemoveFocusController extends FormController {
setup() {
super.setup();
onMounted(() => {
this.__owl__.bdom.el.querySelectorAll(':focus').forEach(element => element.blur());
})
}
}
registry.category('views').add('remove_focus_view', {
...formView,
Controller: RemoveFocusController,
});

View File

@@ -16,7 +16,7 @@
<record model="ir.ui.view" id="mrs_production_process_parameter_form"> <record model="ir.ui.view" id="mrs_production_process_parameter_form">
<field name="model">sf.production.process.parameter</field> <field name="model">sf.production.process.parameter</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<form string="表面工艺可选参数" create="0" delete="0" > <form string="表面工艺可选参数" create="0" delete="0">
<sheet> <sheet>
<div class="oe_title"> <div class="oe_title">
<h1> <h1>
@@ -33,12 +33,11 @@
<group> <group>
<field name="processing_day" readonly="1"/> <field name="processing_day" readonly="1"/>
<field name="travel_day" readonly="1"/> <field name="travel_day" readonly="1"/>
<field name="processing_mm" readonly="1"/>
</group> </group>
</group> </group>
<notebook> <notebook>
<page string="适用材料"> <page string="适用材料">
<field name="materials_model_ids" readonly="1"></field> <field name="materials_model_ids"></field>
</page> </page>
</notebook> </notebook>
</sheet> </sheet>
@@ -53,7 +52,7 @@
<search> <search>
<filter name="filter_active" string="已归档" domain="[('active','=',False)]"/> <filter name="filter_active" string="已归档" domain="[('active','=',False)]"/>
<field name="name" string="名称" filter_domain="[('name','ilike',self)]"/> <field name="name" string="名称" filter_domain="[('name','ilike',self)]"/>
<field name="code" string="编码" filter_domain="[('code','ilike',self)]"/> <field name="code" string="编码" filter_domain="[('codeNum','ilike',self)]"/>
<searchpanel class="account_root"> <searchpanel class="account_root">
<field name="process_id" icon="fa-filter"/> <field name="process_id" icon="fa-filter"/>
</searchpanel> </searchpanel>
@@ -141,7 +140,7 @@
<field name="model">sf.production.process.category</field> <field name="model">sf.production.process.category</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree string="表面工艺类别" default_order="sequence, id" create="0" edit="0" delete="1"> <tree string="表面工艺类别" default_order="sequence, id" create="0" edit="0" delete="1">
<field name="sequence" widget="handle" string="序号" readonly="1"/> <field name="sequence" widget="handle" string="序号"/>
<field name="code"/> <field name="code"/>
<field name="name" string="名称"/> <field name="name" string="名称"/>
</tree> </tree>
@@ -164,8 +163,7 @@
<record model="ir.ui.view" id="sf_production_process_tree"> <record model="ir.ui.view" id="sf_production_process_tree">
<field name="model">sf.production.process</field> <field name="model">sf.production.process</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree string="表面工艺" create="0" edit="0" delete="0"> <tree string="表面工艺" create="0" edit="0" delete="1">
<field name="sequence" string="加工顺序" readonly="1"/>
<field name="code"/> <field name="code"/>
<field name="name" string="名称"/> <field name="name" string="名称"/>
<field name="remark"/> <field name="remark"/>
@@ -176,7 +174,7 @@
<record model="ir.ui.view" id="sf_production_process_form"> <record model="ir.ui.view" id="sf_production_process_form">
<field name="model">sf.production.process</field> <field name="model">sf.production.process</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<form string="表面工艺" create="0" delete="0"> <form string="表面工艺" create="0" edit="1" delete="1">
<sheet> <sheet>
<div class="oe_title"> <div class="oe_title">
<h1> <h1>
@@ -194,11 +192,11 @@
</group> </group>
</group> </group>
<notebook> <notebook>
<page string="可选参数" > <page string="可选参数">
<field name="parameter_ids" > <field name="parameter_ids">
<tree force_save="1" create="0"> <tree force_save="1">
<field name="code" readonly="1" force_save="1"/> <field name="code" readonly="1" force_save="1"/>
<field name="name" readonly="1"/> <field name="name"/>
<field name="gain_way"/> <field name="gain_way"/>
<field name='process_id' default="default"/> <field name='process_id' default="default"/>
</tree> </tree>

View File

@@ -171,7 +171,7 @@
<field name="width"/> <field name="width"/>
<field name="height"/> <field name="height"/>
<field name="diameter"/> <field name="diameter"/>
<field name="weight" string="重量(kg)"/> <field name="weight"/>
<field name="orientation_dish_diameter"/> <field name="orientation_dish_diameter"/>
<field name="clamping_diameter"/> <field name="clamping_diameter"/>
<field name="clamping_num"/> <field name="clamping_num"/>
@@ -197,7 +197,7 @@
<field name="width"/> <field name="width"/>
<field name="height"/> <field name="height"/>
<field name="diameter"/> <field name="diameter"/>
<field name="weight" string="重量(kg)"/> <field name="weight"/>
<field name="clamping_diameter"/> <field name="clamping_diameter"/>
<field name="connector_diameter"/> <field name="connector_diameter"/>
<field name="chucking_power_max"/> <field name="chucking_power_max"/>
@@ -220,7 +220,7 @@
<field name="length"/> <field name="length"/>
<field name="width"/> <field name="width"/>
<field name="height"/> <field name="height"/>
<field name="weight" string="重量(kg)"/> <field name="weight"/>
<field name="gripper_length_min"/> <field name="gripper_length_min"/>
<field name="gripper_width_min"/> <field name="gripper_width_min"/>
<field name="gripper_height_min"/> <field name="gripper_height_min"/>
@@ -248,7 +248,7 @@
<field name="length"/> <field name="length"/>
<field name="width"/> <field name="width"/>
<field name="height"/> <field name="height"/>
<field name="weight" string="重量(kg)"/> <field name="weight"/>
<field name="gripper_length_min"/> <field name="gripper_length_min"/>
<field name="gripper_width_min"/> <field name="gripper_width_min"/>
<field name="gripper_height_min"/> <field name="gripper_height_min"/>
@@ -278,7 +278,7 @@
<field name="width"/> <field name="width"/>
<field name="height"/> <field name="height"/>
<field name="height_tolerance_value"/> <field name="height_tolerance_value"/>
<field name="weight" string="重量(kg)"/> <field name="weight"/>
<field name="gripper_length_min"/> <field name="gripper_length_min"/>
<field name="gripper_width_min"/> <field name="gripper_width_min"/>
<field name="gripper_height_min"/> <field name="gripper_height_min"/>
@@ -307,7 +307,7 @@
<field name="length"/> <field name="length"/>
<field name="width"/> <field name="width"/>
<field name="height"/> <field name="height"/>
<field name="weight" string="重量(kg)"/> <field name="weight"/>
<field name="gripper_length_min"/> <field name="gripper_length_min"/>
<field name="gripper_width_min"/> <field name="gripper_width_min"/>
<field name="gripper_height_min"/> <field name="gripper_height_min"/>
@@ -335,7 +335,7 @@
<field name="width"/> <field name="width"/>
<field name="height"/> <field name="height"/>
<field name="diameter"/> <field name="diameter"/>
<field name="weight" string="重量(kg)"/> <field name="weight"/>
<field name="gripper_length_min"/> <field name="gripper_length_min"/>
<field name="gripper_width_min"/> <field name="gripper_width_min"/>
<field name="gripper_height_min"/> <field name="gripper_height_min"/>

View File

@@ -36,7 +36,7 @@ class Http(models.AbstractModel):
post_time = int(datas['HTTP_TIMESTAMP']) post_time = int(datas['HTTP_TIMESTAMP'])
datetime_post = datetime.fromtimestamp(post_time) datetime_post = datetime.fromtimestamp(post_time)
datetime_now = datetime.now().replace(microsecond=0) datetime_now = datetime.now().replace(microsecond=0)
datetime_del = datetime_now + timedelta(seconds=30) datetime_del = datetime_now + timedelta(seconds=5)
if datetime_post > datetime_del: if datetime_post > datetime_del:
raise AuthenticationError('请求已过期') raise AuthenticationError('请求已过期')
check_str = '%s%s%s' % (datas['HTTP_TOKEN'], post_time, factory_secret.sf_secret_key) check_str = '%s%s%s' % (datas['HTTP_TOKEN'], post_time, factory_secret.sf_secret_key)

View File

@@ -1,8 +1,7 @@
from datetime import datetime from datetime import datetime
import logging import logging
import requests import requests
from odoo.exceptions import UserError from odoo import fields, models
from odoo import fields, models, _
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
@@ -15,49 +14,26 @@ class StatusChange(models.Model):
def action_confirm(self): def action_confirm(self):
# 在原有方法执行前记录日志和执行其他操作 # 在原有方法执行前记录日志和执行其他操作
logging.info('函数已经执行=============') logging.info('函数已经执行=============')
server_product_none = []
for order in self.order_line:
gain_way_no = order.product_template_id.model_process_parameters_ids.filtered(lambda a: not a.gain_way)
if gain_way_no:
process_parameters = [item.name for item in gain_way_no]
raise UserError(
_("请先至【制造】-【配置】中【表面工艺可选参数】为【%s】填写获取方式", ", ".join(process_parameters)))
for item in order.product_template_id.model_process_parameters_ids:
if item.gain_way == '外协':
server_product = self.env['product.template'].search(
[('server_product_process_parameters_id', '=', item.id),
('detailed_type', '=', 'service')])
if not server_product:
server_product_none.append(item.name)
if server_product_none:
raise UserError(_("请先至【产品】中创建【表面工艺参数】为【%s】的服务产品", ", ".join(server_product_none)))
# 使用super()来调用原始方法(在本例中为'sale.order'模型的'action_confirm'方法) # 使用super()来调用原始方法(在本例中为'sale.order'模型的'action_confirm'方法)
try: res = super(StatusChange, self).action_confirm()
res = super(StatusChange, self).action_confirm()
# 原有方法执行后进行额外的操作如调用外部API # 原有方法执行后进行额外的操作如调用外部API
process_start_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') process_start_time = str(datetime.now())
config = self.env['res.config.settings'].get_values() config = self.env['res.config.settings'].get_values()
json1 = { json1 = {
'params': { 'params': {
'model_name': 'jikimo.process.order', 'model_name': 'jikimo.process.order',
'field_name': 'name', 'field_name': 'name',
'default_code': self.default_code, 'default_code': self.default_code,
'state': '加工中', 'state': '加工中',
'process_start_time': process_start_time, 'process_start_time': process_start_time,
}, },
} }
url1 = config['bfm_url_new'] + '/api/get/state/get_order' url1 = config['bfm_url_new'] + '/api/get/state/get_order'
ret = requests.post(url1, json=json1, data=None) requests.post(url1, json=json1, data=None)
ret = ret.json() logging.info('接口已经执行=============')
if not ret.get('error'):
logging.info('接口已经执行=============')
else:
logging.error('工厂加工同步订单状态失败 {}'.format(ret.text))
raise UserError('工厂加工同步订单状态失败')
except UserError as e:
logging.error('工厂加工同步订单状态失败 {}'.format(e))
raise UserError('工厂加工同步订单状态失败')
return res return res
def action_cancel(self): def action_cancel(self):
@@ -226,12 +202,12 @@ class FinishStatusChange(models.Model):
[('id', 'child_of', self.picking_type_id.warehouse_id.view_location_id.id), [('id', 'child_of', self.picking_type_id.warehouse_id.view_location_id.id),
('usage', '!=', 'supplier')]) ('usage', '!=', 'supplier')])
if self.env['stock.move'].search([ if self.env['stock.move'].search([
('state', 'in', ['confirmed', 'partially_available', 'waiting', 'assigned']), ('state', 'in', ['confirmed', 'partially_available', 'waiting', 'assigned']),
('product_qty', '>', 0), ('product_qty', '>', 0),
('location_id', 'in', wh_location_ids), ('location_id', 'in', wh_location_ids),
('move_orig_ids', '=', False), ('move_orig_ids', '=', False),
('picking_id', 'not in', self.ids), ('picking_id', 'not in', self.ids),
('product_id', 'in', lines.product_id.ids)], limit=1): ('product_id', 'in', lines.product_id.ids)], limit=1):
action = self.action_view_reception_report() action = self.action_view_reception_report()
action['context'] = {'default_picking_ids': self.ids} action['context'] = {'default_picking_ids': self.ids}
return action return action

View File

@@ -122,7 +122,7 @@ class ResMrpBomMo(models.Model):
# 查bom的原材料 # 查bom的原材料
def get_raw_bom(self, product): def get_raw_bom(self, product):
raw_bom = self.env['product.product'].search( raw_bom = self.env['product.product'].search(
[('categ_id.type', '=', '原材料'), ('materials_type_id', '=', product.materials_type_id.id)],limit=1) [('categ_id.type', '=', '原材料'), ('materials_type_id', '=', product.materials_type_id.id)])
return raw_bom return raw_bom

View File

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

View File

@@ -1,22 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
'name': '机企猫智能工厂 员工管理',
'version': '1.0',
'summary': '智能工厂员工模块',
'sequence': 1,
'category': 'sf',
'website': 'https://www.sf.jikimo.com',
'depends': ['hr'],
'data': [
'views/hr_employee.xml',
],
'demo': [
],
'qweb': [
],
'license': 'LGPL-3',
'installable': True,
'application': False,
'auto_install': False,
}

View File

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

View File

@@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<data>
<record id="view_form_employee_extend" model="ir.ui.view">
<field name="name">employee_form</field>
<field name="model">hr.employee</field>
<field name="inherit_id" ref="hr.view_employee_form"/>
<field name="arch" type="xml">
<xpath expr="//group//field[@name='work_email']" position="attributes">
<attribute name="required">1</attribute>
</xpath>
</field>
</record>
</data>
</odoo>

View File

@@ -30,7 +30,6 @@
'views/machine_info_present.xml', 'views/machine_info_present.xml',
'views/delivery_record.xml', 'views/delivery_record.xml',
'views/res_config_settings_views.xml', 'views/res_config_settings_views.xml',
'views/maintenance_views.xml',
], ],
'assets': { 'assets': {

View File

@@ -1,55 +1,10 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import re
import ast import ast
import json import json
import base64
import logging import logging
import psycopg2
from datetime import datetime, timedelta
from odoo import http from odoo import http
from odoo.http import request from odoo.http import request
# 数据库连接配置
db_config = {
"database": "timeseries_db",
"user": "postgres",
"password": "postgres",
"port": "5432",
"host": "172.16.10.98"
}
def convert_to_seconds(time_str):
# 修改正则表达式,使 H、M、S 部分可选
pattern = r"(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?"
match = re.match(pattern, time_str)
if match:
# 提取各时间单位如果某个单位缺失则默认设为0
hours = int(match.group(1)) if match.group(1) else 0
minutes = int(match.group(2)) if match.group(2) else 0
seconds = int(match.group(3)) if match.group(3) else 0
# 计算总秒数
total_seconds = hours * 3600 + minutes * 60 + seconds
if total_seconds == 0:
# return None
pattern = r"(?:(\d+)小时)?(?:(\d+)分钟)?(?:(\d+)秒)?"
match = re.match(pattern, time_str)
if match:
# 提取各时间单位如果某个单位缺失则默认设为0
hours = int(match.group(1)) if match.group(1) else 0
minutes = int(match.group(2)) if match.group(2) else 0
seconds = int(match.group(3)) if match.group(3) else 0
# 计算总秒数
total_seconds = hours * 3600 + minutes * 60 + seconds
return total_seconds
else:
return None
return total_seconds
class Sf_Dashboard_Connect(http.Controller): class Sf_Dashboard_Connect(http.Controller):
@@ -63,11 +18,6 @@ class Sf_Dashboard_Connect(http.Controller):
""" """
res = {'status': 1, 'message': '成功', 'data': []} res = {'status': 1, 'message': '成功', 'data': []}
logging.info('前端请求机床数据的参数为:%s' % kw) logging.info('前端请求机床数据的参数为:%s' % kw)
# 获取当前时间的时间戳
current_timestamp = datetime.now().timestamp()
print(current_timestamp)
# tem_list = [ # tem_list = [
# "XT-GNJC-WZZX-X800-Y550-Z550-T24-A5-1", "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-3", # "XT-GNJC-WZZX-X800-Y550-Z550-T24-A5-1", "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-3",
# "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-4", "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-5", # "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-4", "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-5",
@@ -83,24 +33,10 @@ class Sf_Dashboard_Connect(http.Controller):
machine_list = ast.literal_eval(kw['machine_list']) machine_list = ast.literal_eval(kw['machine_list'])
for item in machine_list: for item in machine_list:
machine_data = equipment_obj.search([('code', '=', item)]) machine_data = equipment_obj.search([('code', '=', item)])
# 机床上线时间段
first_online_duration = current_timestamp - int(machine_data.first_online_time.timestamp())
power_off_time = None
power_off_rate = None
if machine_data.machine_power_on_time:
power_off_time = first_online_duration - convert_to_seconds(machine_data.machine_power_on_time)
power_off_rate = round((power_off_time / first_online_duration), 3)
else:
power_off_time = False
power_off_rate = False
if machine_data: if machine_data:
res['data'].append({ res['data'].append({
'active': machine_data.status,
'id': machine_data.id, 'id': machine_data.id,
'name': machine_data.name, 'name': machine_data.name,
'brand': machine_data.type_id.name,
'code': machine_data.code, 'code': machine_data.code,
'status': machine_data.status, 'status': machine_data.status,
'run_status': machine_data.run_status, 'run_status': machine_data.run_status,
@@ -152,681 +88,11 @@ class Sf_Dashboard_Connect(http.Controller):
'alarm_time': machine_data.alarm_time, 'alarm_time': machine_data.alarm_time,
'alarm_msg': machine_data.alarm_msg, 'alarm_msg': machine_data.alarm_msg,
'clear_time': machine_data.clear_time, 'clear_time': machine_data.clear_time,
# 计算出来的数据
# 开动率:运行时间/通电时间
'run_rate': machine_data.run_rate,
# 关机时长:初次上线时间 - 通电时间
'power_off_time': power_off_time,
# 关机率:关机时长/初次上线时间
'power_off_rate': power_off_rate,
'first_online_duration': first_online_duration,
# 停机时间:关机时间 - 运行时间
# 停机时长:关机时间 - 初次上线时间
'img': f'data:image/png;base64,{machine_data.machine_tool_picture.decode("utf-8")}',
'equipment_type': machine_data.category_id.name,
}) })
return json.dumps(res) return json.JSONEncoder().encode(res)
except Exception as e: except Exception as e:
logging.info('前端请求机床数据失败,原因:%s' % e) logging.info('前端请求机床数据失败,原因:%s' % e)
res['status'] = -1 res['status'] = -1
res['message'] = '前端请求机床数据失败,原因:%s' % e res['message'] = '前端请求机床数据失败,原因:%s' % e
return json.JSONEncoder().encode(res) return json.JSONEncoder().encode(res)
@http.route('/api/logs/list', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
def logs_list(self, **kw):
"""
拿到日志数据返回给大屏展示
:param kw:
:return:
"""
res = {'status': 1, 'message': '成功', 'data': {}}
logging.info('前端请求日志数据的参数为:%s' % kw)
try:
# 连接数据库
conn = psycopg2.connect(**db_config)
cur = conn.cursor()
machine_list = ast.literal_eval(kw['machine_list'])
begin_time_str = kw['begin_time'].strip('"')
end_time_str = kw['end_time'].strip('"')
begin_time = datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S')
end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S')
print('begin_time: %s' % begin_time)
for item in machine_list:
sql = '''
SELECT time, device_state, program_name
FROM device_data
WHERE device_name = %s AND time >= %s AND time <= %s
ORDER BY time DESC;
'''
# 执行SQL命令使用参数绑定
cur.execute(sql, (item, begin_time, end_time))
results = cur.fetchall()
# 将数据按照 equipment_code 进行分组
if item not in res['data']:
res['data'][item] = []
for result in results:
res['data'][item].append({
'time': result[0].strftime('%Y-%m-%d %H:%M:%S'),
'state': result[1],
'production_name': result[2],
})
return json.dumps(res) # 注意使用 json.dumps 而不是直接用 json.JSONEncoder().encode()
except Exception as e:
logging.info('前端请求日志数据失败,原因:%s' % e)
res['status'] = -1
res['message'] = '前端请求日志数据失败,原因:%s' % e
return json.dumps(res)
# 返回CNC机床列表
@http.route('/api/CNCList', type='http', auth='public', methods=['GET', 'POST'], csrf=False,
cors="*")
def CNCList(self, **kw):
"""
获取CNC机床列表
:param kw:
:return:
"""
# logging.info('CNCList:%s' % kw)
try:
res = {'Succeed': True}
# cnc_list = request.env['sf.cnc.equipment'].sudo().search([])
# cnc_list = ["XT-GNJC-WZZX-X800-Y550-Z550-T24-A5-1", "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-3",
# "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-4", "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-5",
# "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-6", "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-7",
# "XT-GNJC-LSZX-X800-Y550-Z550-T24-A3-8", "XT-GNJC-WZZX-X800-Y550-Z550-T24-A5-2",
# "XT-GNJC-GSZG-X600-Y400-Z350-T21-A3-9", "XT-GNJC-GSZG-X600-Y400-Z350-T21-A3-10",
# "XT-GNJC-GSZG-X600-Y400-Z350-T21-A3-11", "XT-GNJC-GSZG-X600-Y400-Z350-T21-A3-12",
# "XT-GNJC-GSZG-X600-Y400-Z350-T21-A3-13", "XT-GNJC-GSZG-X600-Y400-Z350-T21-A3-14"]
cnc_list_obj = request.env['maintenance.equipment'].sudo().search(
[('function_type', '!=', False), ('active', '=', True)])
cnc_list = list(map(lambda x: x.code, cnc_list_obj))
print('cnc_list: %s' % cnc_list)
res['CNCList'] = cnc_list
except Exception as e:
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
logging.info('CNCList error:%s' % e)
return json.JSONEncoder().encode(res)
# 返回产线列表
@http.route('/api/LineList', type='http', auth='public', methods=['GET', 'POST'], csrf=False,
cors="*")
def LineList(self, **kw):
"""
获取产线列表
:param kw:
:return:
"""
try:
res = {'Succeed': True}
line_list_obj = request.env['sf.production.line'].sudo().search([('name', 'ilike', 'CNC')])
line_list = list(map(lambda x: x.name, line_list_obj))
print('line_list: %s' % line_list)
res['LineList'] = line_list
except Exception as e:
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
logging.info('LineList error:%s' % e)
return json.JSONEncoder().encode(res)
# 获取产线产量相关
@http.route('/api/LineProduct', type='http', auth='public', methods=['GET', 'POST'], csrf=False,
cors="*")
def LineProduct(self, **kw):
"""
获取产线产量相关
:param kw:
:return:
"""
res = {'status': 1, 'message': '成功', 'data': {}}
logging.info('前端请求产线产量数据的参数为:%s' % kw)
try:
plan_obj = request.env['sf.production.plan'].sudo()
line_list = ast.literal_eval(kw['line_list'])
print('line_list: %s' % line_list)
for line in line_list:
plan_data = plan_obj.search([('production_line_id.name', '=', line)])
# 工单总量
plan_data_total_counts = plan_obj.search_count([('production_line_id.name', '=', line)])
# 工单完成量
plan_data_finish_counts = plan_obj.search_count(
[('production_line_id.name', '=', line), ('state', 'in', ['finished'])])
# 工单计划量
plan_data_plan_counts = plan_obj.search_count(
[('production_line_id.name', '=', line), ('state', 'not in', ['finished'])])
# 工单不良累计
plan_data_fault_counts = plan_obj.search_count(
[('production_line_id.name', '=', line), ('production_id.state', 'in', ['scrap', 'cancel'])])
# 工单返工数量
plan_data_rework_counts = plan_obj.search_count(
[('production_line_id.name', '=', line), ('production_id.state', 'in', ['rework'])])
# 工单完成率
finishe_rate = round(
(plan_data_finish_counts / plan_data_total_counts if plan_data_total_counts > 0 else 0), 3)
# 工单进度偏差
plan_data_progress_deviation = plan_data_finish_counts - plan_data_plan_counts
if plan_data:
data = {
'plan_data_total_counts': plan_data_total_counts,
'plan_data_finish_counts': plan_data_finish_counts,
'plan_data_plan_counts': plan_data_plan_counts,
'plan_data_fault_counts': plan_data_fault_counts,
'finishe_rate': finishe_rate,
'plan_data_progress_deviation': plan_data_progress_deviation,
'plan_data_rework_counts': plan_data_rework_counts
}
res['data'][line] = data
return json.dumps(res) # 注意使用 json.dumps 而不是直接用 json.JSONEncoder().encode()
except Exception as e:
logging.info('前端请求产线产量数据失败,原因:%s' % e)
res['status'] = -1
res['message'] = '前端请求产线产量数据失败,原因:%s' % e
return json.dumps(res)
# 日完成量统计
@http.route('/api/DailyFinishCount', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
def DailyFinishCount(self, **kw):
"""
获取日完成量统计
:param kw:
:return:
"""
res = {'status': 1, 'message': '成功', 'data': {}}
plan_obj = request.env['sf.production.plan'].sudo()
line_list = ast.literal_eval(kw['line_list'])
begin_time_str = kw['begin_time'].strip('"')
end_time_str = kw['end_time'].strip('"')
begin_time = datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S')
end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S')
print('line_list: %s' % line_list)
def get_date_list(start_date, end_date):
date_list = []
current_date = start_date
while current_date <= end_date:
date_list.append(current_date)
current_date += timedelta(days=1)
return date_list
for line in line_list:
date_list = get_date_list(begin_time, end_time)
order_counts = []
date_field_name = 'actual_end_time' # 替换为你模型中的实际字段名
for date in date_list:
next_day = date + timedelta(days=1)
orders = plan_obj.search([('production_line_id.name', '=', line), ('state', 'in', ['finished']),
(date_field_name, '>=', date.strftime('%Y-%m-%d 00:00:00')),
(date_field_name, '<', next_day.strftime('%Y-%m-%d 00:00:00'))
])
rework_orders = plan_obj.search(
[('production_line_id.name', '=', line), ('production_id.state', 'in', ['rework']),
(date_field_name, '>=', date.strftime('%Y-%m-%d 00:00:00')),
(date_field_name, '<', next_day.strftime('%Y-%m-%d 00:00:00'))
])
not_passed_orders = plan_obj.search(
[('production_line_id.name', '=', line), ('production_id.state', 'in', ['scrap', 'cancel']),
(date_field_name, '>=', date.strftime('%Y-%m-%d 00:00:00')),
(date_field_name, '<', next_day.strftime('%Y-%m-%d 00:00:00'))
])
order_counts.append({
'date': date.strftime('%Y-%m-%d'),
'order_count': len(orders),
'rework_orders': len(rework_orders),
'not_passed_orders': len(not_passed_orders)
})
# 外面包一层没什么是包一层不能解决的包一层就能区分了类似于包一层div
# 外面包一层的好处是,可以把多个数据结构打包在一起,方便前端处理
# date_list_dict = {line: order_counts}
res['data'][line] = order_counts
return json.dumps(res)
# 实时产量
@http.route('/api/RealTimeProduct', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
def RealTimeProduct(self, **kw):
"""
获取实时产量
:param kw:
:return:
"""
res = {'status': 1, 'message': '成功', 'data': {}}
plan_obj = request.env['sf.production.plan'].sudo()
line_list = ast.literal_eval(kw['line_list'])
begin_time_str = kw['begin_time'].strip('"')
end_time_str = kw['end_time'].strip('"')
begin_time = datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S')
end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S')
def get_hourly_intervals(start_time, end_time):
intervals = []
current_time = start_time
while current_time < end_time:
next_hour = current_time + timedelta(hours=1)
intervals.append((current_time, min(next_hour, end_time)))
current_time = next_hour
return intervals
# 当班计划量
for line in line_list:
plan_order_nums = plan_obj.search_count(
[('production_line_id.name', '=', line), ('state', 'not in', ['draft']),
('date_planned_start', '>=', begin_time),
('date_planned_start', '<', end_time)
])
finish_order_nums = plan_obj.search_count(
[('production_line_id.name', '=', line), ('state', 'in', ['finished']),
('date_planned_start', '>=', begin_time),
('date_planned_start', '<', end_time)
])
hourly_intervals = get_hourly_intervals(begin_time, end_time)
production_counts = []
for start, end in hourly_intervals:
orders = plan_obj.search([
('actual_end_time', '>=', start.strftime('%Y-%m-%d %H:%M:%S')),
('actual_end_time', '<', end.strftime('%Y-%m-%d %H:%M:%S')),
('production_line_id.name', '=', line)
])
production_counts.append({
'start_time': start.strftime('%Y-%m-%d %H:%M:%S'),
'end_time': end.strftime('%Y-%m-%d %H:%M:%S'),
'production_count': len(orders)
})
production_counts_dict = {'production_counts': production_counts,
'plan_order_nums': plan_order_nums,
'finish_order_nums': finish_order_nums,
}
res['data'][line] = production_counts_dict
# res['data'].append({line: production_counts})
return json.dumps(res)
# 工单明细
@http.route('/api/OrderDetail', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
def OrderDetail(self, **kw):
"""
获取工单明细
:param kw:
:return:
"""
# res = {'status': 1, 'message': '成功', 'not_done_data': [], 'done_data': []}
res = {'status': 1, 'message': '成功', 'data': {}}
plan_obj = request.env['sf.production.plan'].sudo()
line_list = ast.literal_eval(kw['line_list'])
begin_time_str = kw['begin_time'].strip('"')
end_time_str = kw['end_time'].strip('"')
begin_time = datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S')
end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S')
print('line_list: %s' % line_list)
not_done_data = []
done_data = []
final_data = {}
for line in line_list:
# 未完成订单
not_done_orders = plan_obj.search(
[('production_line_id.name', '=', line), ('state', 'not in', ['finished'])])
print(not_done_orders)
# 完成订单
finish_orders = plan_obj.search([('production_line_id.name', '=', line), ('state', 'in', ['finished'])])
print(finish_orders)
# 获取所有未完成订单的ID列表
order_ids = [order.id for order in not_done_orders]
# 获取所有已完成订单的ID列表
finish_order_ids = [order.id for order in finish_orders]
# 对ID进行排序
sorted_order_ids = sorted(order_ids)
finish_sorted_order_ids = sorted(finish_order_ids)
# 创建ID与序号的对应关系
id_to_sequence = {order_id: index + 1 for index, order_id in enumerate(sorted_order_ids)}
finish_id_to_sequence = {order_id: index + 1 for index, order_id in enumerate(finish_sorted_order_ids)}
# # 输出结果或进一步处理
# for order_id, sequence in id_to_sequence.items():
# print(f"Order ID: {order_id} - Sequence: {sequence}")
for order in not_done_orders:
blank_name = ''
try:
blank_name = order.production_id.move_raw_ids[0].product_id.name
except:
continue
# blank_name = 'R-S00109-1 [碳素结构钢 Q235-118.0 * 72.0 * 21.0]'
# 正则表达式
material_pattern = r'\[(.*?)-' # 从 [ 开始,碰到 - 停止
dimensions = blank_name.split('-')[-1].split(']')[0]
# 匹配材料名称
material_match = re.search(material_pattern, blank_name)
material = material_match.group(1) if material_match else 'No match found'
state_dict = {
'draft': '待排程',
'done': '已排程',
'processing': '生产中',
'finished': '已完成'
}
line_dict = {
'sequence': id_to_sequence[order.id],
'workorder_name': order.name,
'blank_name': blank_name,
'material': material,
'dimensions': dimensions,
'order_qty': order.product_qty,
'state': state_dict[order.state],
}
not_done_data.append(line_dict)
for finish_order in finish_orders:
blank_name = ''
try:
blank_name = finish_order.production_id.move_raw_ids[0].product_id.name
except:
continue
material_pattern = r'\[(.*?)-' # 从 [ 开始,碰到 - 停止
dimensions = blank_name.split('-')[-1].split(']')[0]
# 匹配材料名称
material_match = re.search(material_pattern, blank_name)
material = material_match.group(1) if material_match else 'No match found'
line_dict = {
'sequence': finish_id_to_sequence[finish_order.id],
'workorder_name': finish_order.name,
'blank_name': blank_name,
'material': material,
'dimensions': dimensions,
'order_qty': finish_order.product_qty,
'finish_time': finish_order.actual_end_time.strftime('%Y-%m-%d %H:%M:%S'),
}
done_data.append(line_dict)
# 开始包一层
res['data'][line] = {'not_done_data': not_done_data, 'done_data': done_data}
return json.dumps(res)
# 查询pg库来获得待机次数
@http.route('/api/IdleAlarmCount', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
def idle_alarm_count(self, **kw):
"""
查询设备的待机次数
"""
res = {'status': 1, 'message': '成功', 'data': {}}
logging.info('前端请求机床数据的参数为:%s' % kw)
# 连接数据库
conn = psycopg2.connect(**db_config)
cur = conn.cursor()
try:
# 获取请求的机床数据
machine_list = ast.literal_eval(kw['machine_list'])
total_alarm_time = 0
alarm_count_num = 0
for item in machine_list:
sql = '''
SELECT COUNT(*)
FROM (
SELECT DISTINCT ON (idle_start_time) idle_start_time
FROM device_data
WHERE device_name = %s AND idle_start_time IS NOT NULL
ORDER BY idle_start_time, time
) subquery;
'''
sql2 = '''
SELECT DISTINCT ON (alarm_time) alarm_time, alarm_repair_time
FROM device_data
WHERE device_name = %s AND alarm_time IS NOT NULL
ORDER BY alarm_time, time;
'''
# 执行SQL命令
cur.execute(sql, (item,))
result = cur.fetchall()
print('result========', result)
cur.execute(sql2, (item,))
result2 = cur.fetchall()
print('result2========', result2)
#
for row in result:
res['data'][item] = {'idle_count': row[0]}
alarm_count = []
for row in result2:
alarm_count.append(row[0])
total_alarm_time += abs(float(row[0]))
if len(list(set(alarm_count))) == 1:
if list(set(alarm_count))[0] is None:
alarm_count_num = 0
else:
alarm_count_num = 1
else:
alarm_count_num = len(list(set(alarm_count)))
res['data'][item]['total_alarm_time'] = total_alarm_time / 3600
res['data'][item]['alarm_count_num'] = alarm_count_num
# 返回统计结果
return json.dumps(res)
except Exception as e:
print(f"An error occurred: {e}")
return json.dumps(res)
finally:
cur.close()
conn.close()
# 查询pg库来获得异常情况
@http.route('/api/alarm/logs', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
def alarm_logs(self, **kw):
"""
查询设备的异常情况
"""
res = {'status': 1, 'message': '成功', 'data': {}}
logging.info('前端请求机床数据的参数为:%s' % kw)
# 连接数据库
conn = psycopg2.connect(**db_config)
cur = conn.cursor()
try:
# 获取请求的机床数据
# machine_list = ast.literal_eval(kw['machine_list'])
# idle_times = []
# idle_dict = {}
# for item in machine_list:
sql = '''
SELECT DISTINCT ON (alarm_time) alarm_time, alarm_message, system_date, system_time, alarm_repair_time
FROM device_data
WHERE alarm_time IS NOT NULL
ORDER BY alarm_time, time;
'''
# 执行SQL命令
cur.execute(sql)
result = cur.fetchall()
print('result', result)
# 将查询结果转换为字典列表
data = []
for row in result:
record = {
'alarm_time': row[0],
'alarm_message': row[1],
'system_date': row[2],
'system_time': row[3],
'alarm_repair_time': row[4]
}
data.append(record)
# 将数据填充到返回结果中
res['data'] = data
# 返回统计结果
return json.dumps(res, ensure_ascii=False)
except Exception as e:
print(f"An error occurred: {e}")
return json.dumps(res)
finally:
cur.close()
conn.close()
# 设备oee
@http.route('/api/OEE', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
def OEE(self, **kw):
"""
获取产线等oee
"""
res = {'status': 1, 'message': '成功', 'data': {}}
logging.info('前端请求oee数据的参数为:%s' % kw)
try:
count_oee = 1
workcenter_obj = request.env['mrp.workcenter'].sudo()
workcenter_list = ast.literal_eval(kw['workcenter_list'])
print('workcenter_list: %s' % workcenter_list)
for line in workcenter_list:
res['data'][line] = workcenter_obj.search([('name', '=', line)]).oee
count_oee *= workcenter_obj.search([('name', '=', line)]).oee
res['data']['综合oee'] = count_oee / 1000000
except Exception as e:
print(f"An error occurred: {e}")
return json.dumps(res)
# # 查询某段时间的设备oee
# @http.route('/api/OEEByTime', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
# def OEEByTime(self, **kw):
# """
# 获取某段时间的oee
# """
# res = {'status': 1, 'message': '成功', 'data': {}}
# logging.info('前端请求获取某段时间的oee的参数为:%s' % kw)
# workcenter_list = ast.literal_eval(kw['workcenter_list'])
# begin_time_str = kw['begin_time'].strip('"')
# end_time_str = kw['end_time'].strip('"')
# begin_time = datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S')
# end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S')
# print('workcenter_list: %s' % workcenter_list)
# # 连接数据库
# conn = psycopg2.connect(**db_config)
# cur = conn.cursor()
# # 查询并计算OEE平均值
# oee_data = {}
# for workcenter in workcenter_list:
# cur.execute("""
# SELECT AVG(oee) as avg_oee
# FROM oee_data
# WHERE workcenter_name = %s
# AND time BETWEEN %s AND %s
# """, (workcenter, begin_time, end_time))
#
# result = cur.fetchone()
# avg_oee = result[0] if result else 0.0
# oee_data[workcenter] = avg_oee
#
# # 返回数据
# res['data'] = oee_data
# return json.dumps(res)
@http.route('/api/OEEByTime', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
def OEEByTime(self, **kw):
"""
获取某段时间的OEE根据用户指定的时间单位day或hour返回对应的平均值。
如果不传time_unit则默认按天返回并补全没有数据的时间段填充0值。
"""
res = {'status': 1, 'message': '成功', 'data': {}}
logging.info('前端请求获取某段时间的OEE的参数为:%s' % kw)
# 获取并解析参数
workcenter_list = ast.literal_eval(kw['workcenter_list'])
begin_time_str = kw['begin_time'].strip('"')
end_time_str = kw['end_time'].strip('"')
time_unit = kw.get('time_unit', 'day') # 默认单位为天
begin_time = datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S')
end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S')
# 连接数据库
conn = psycopg2.connect(**db_config)
cur = conn.cursor()
# 根据时间单位选择不同的时间格式
if time_unit == 'hour':
time_format = 'YYYY-MM-DD HH24:00:00'
time_delta = timedelta(hours=1)
else: # 默认为'day'
time_format = 'YYYY-MM-DD'
time_delta = timedelta(days=1)
# 查询并计算OEE平均值
oee_data = {}
for workcenter in workcenter_list:
cur.execute(f"""
SELECT to_char(time, '{time_format}') as time_unit, AVG(oee) as avg_oee
FROM oee_data
WHERE workcenter_name = %s
AND time BETWEEN %s AND %s
GROUP BY time_unit
ORDER BY time_unit
""", (workcenter, begin_time, end_time))
results = cur.fetchall()
# 初始化当前产线的OEE数据字典
workcenter_oee = {row[0]: row[1] for row in results}
# 补全缺失的时间段
current_time = begin_time
if time_unit != 'hour':
while current_time <= end_time:
time_key = current_time.strftime('%Y-%m-%d')
if time_key not in workcenter_oee:
workcenter_oee[time_key] = 0
current_time += time_delta
# 按时间排序
oee_data[workcenter] = dict(sorted(workcenter_oee.items()))
# 关闭数据库连接
cur.close()
conn.close()
# 返回数据
res['data'] = oee_data
return json.dumps(res)

View File

@@ -2,4 +2,3 @@ from . import ftp_client
from . import ftp_operate from . import ftp_operate
from . import py2opcua from . import py2opcua
from . import res_config_setting from . import res_config_setting
from . import mrp_workorder

View File

@@ -121,13 +121,6 @@ class Machine_ftp(models.Model):
""" """
_inherit = 'maintenance.equipment' _inherit = 'maintenance.equipment'
# 机床首次上线时间(默认取值2024年08月01日零点)
def _get_default_online_time(self):
return datetime(2024, 1, 1, 0, 0, 0)
first_online_time = fields.Datetime(string='首次上线时间', default=_get_default_online_time)
# workorder_ids = fields.One2many('mrp.workorder', 'machine_tool_id', string='工单') # workorder_ids = fields.One2many('mrp.workorder', 'machine_tool_id', string='工单')
# # 机床配置项目 # # 机床配置项目
@@ -282,28 +275,7 @@ class Machine_ftp(models.Model):
alarm_msg = fields.Char('故障报警信息', readonly=True) alarm_msg = fields.Char('故障报警信息', readonly=True)
clear_time = fields.Char('故障消除时间(复原时间)', readonly=True) clear_time = fields.Char('故障消除时间(复原时间)', readonly=True)
# # 开动率 # 当前程序名, 机床累计运行时间, 机床系统日期, 机床系统时间, 当前刀具号, 机床循环时间
run_rate = fields.Char('开动率', readonly=True)
# 同步CNC设备到oee
def sync_oee(self):
"""
同步CNC设备到oee
:return:
"""
for record in self:
record.ensure_one()
cnc_oee_dict = {
'equipment_id': record.id,
'type_id': record.type_id.id,
'machine_tool_picture': record.machine_tool_picture,
'equipment_code': record.code,
'function_type': record.function_type,
}
if self.env['maintenance.equipment.oee.logs'].search([('equipment_id', '=', record.id)]):
self.env['maintenance.equipment.oee.logs'].write(cnc_oee_dict)
else:
self.env['maintenance.equipment.oee.logs'].create(cnc_oee_dict)
class WorkCenterBarcode(models.Model): class WorkCenterBarcode(models.Model):

View File

@@ -1,38 +0,0 @@
import re
from odoo import fields, models, api
class ResMrpWorkOrder(models.Model):
_inherit = 'mrp.workorder'
mixed_search_field = fields.Char(string='坯料产品名称/RFID')
@api.model
def web_read_group(self, domain, fields, groupby, limit=None, offset=0, orderby=False,
lazy=True, expand=False, expand_limit=None, expand_orderby=False):
domain = domain or []
for index, item in enumerate(domain):
if isinstance(item, list):
if item[0] == 'mixed_search_field':
if self._is_rfid_code(item[2]):
domain[index] = ['rfid_code', item[1], item[2]]
else:
domain[index] = ['product_tmpl_name', item[1], item[2]]
return super(ResMrpWorkOrder, self).web_read_group(domain, fields, groupby, limit=limit, offset=offset, orderby=orderby,
lazy=lazy, expand=expand, expand_limit=expand_limit, expand_orderby=expand_orderby)
def _is_rfid_code(self, tag):
"""
判断是否是rfid_code
"""
# 基于长度判断假设RFID标签长度为10到16个字符
if not 10 <= len(tag) <= 16:
return False
# 基于字符集判断(仅包含数字和字母)
if not re.match("^[0-9]*$", tag):
return False
return True

View File

@@ -26,7 +26,6 @@
<filter string="自动编程" name="no_manual_quotation" domain="[('manual_quotation', '=', False)]"/> <filter string="自动编程" name="no_manual_quotation" domain="[('manual_quotation', '=', False)]"/>
</xpath> </xpath>
<xpath expr="//field[@name='production_id']" position="before"> <xpath expr="//field[@name='production_id']" position="before">
<field name="mixed_search_field"/>
<field name="product_tmpl_name"/> <field name="product_tmpl_name"/>
<field name="rfid_code"/> <field name="rfid_code"/>
</xpath> </xpath>

View File

@@ -18,7 +18,6 @@
<field name="run_status"/> <field name="run_status"/>
<field name="run_time"/> <field name="run_time"/>
<field name="system_date"/> <field name="system_date"/>
<field name="first_online_time"/>
</group> </group>
<group> <group>
<field name="cut_status"/> <field name="cut_status"/>

View File

@@ -1,17 +0,0 @@
<?xml version="1.0"?>
<odoo>
<!-- 修改设备列表视图-->
<record id="sf_machine_hr_equipment_view_tree_inherit" model="ir.ui.view">
<field name="name">sf.machine.hr.equipment.view.tree.inherit</field>
<field name="model">maintenance.equipment</field>
<field name="inherit_id" ref="maintenance.hr_equipment_view_tree"/>
<field name="arch" type="xml">
<xpath expr="//tree" position="inside">
<header>
<button name="sync_oee" type="object" string="同步设备至OEE"/>
</header>
</xpath>
</field>
</record>
</odoo>

View File

@@ -41,32 +41,29 @@ class SfMaintenanceEquipmentOEELog(models.Model):
_name = 'maintenance.equipment.oee.logs' _name = 'maintenance.equipment.oee.logs'
_description = '设备运行日志' _description = '设备运行日志'
equipment_id = fields.Many2one('maintenance.equipment', '机台号', readonly='True') equipment_id = fields.Many2one('maintenance.equipment', '机台号')
equipment_code = fields.Char('设备编码', readonly='True') equipment_code = fields.Char('设备编码')
name = fields.Char('设备名称', readonly='True') name = fields.Char('设备名称', readonly='True')
function_type = fields.Selection(
[("ZXJGZX", "钻铣加工中心"), ("CXJGZX", "车削加工中心"), ("FHJGZX", "复合加工中心")],
default="", string="功能类型")
machine_tool_picture = fields.Binary('设备图片') machine_tool_picture = fields.Binary('设备图片')
type_id = fields.Many2one('sf.machine_tool.type', '品牌型号', reaonly='True') type_id = fields.Many2one('sf.machine_tool.type', '品牌型号')
state = fields.Selection([("加工", "加工"), ("关机", "关机"), ("待机", "待机"), ("故障", "故障"), state = fields.Selection([("加工", "加工"), ("关机", "关机"), ("待机", "待机"), ("故障", "故障"),
("检修", "检修"), ("保养", "保养")], default="", string="实时状态") ("检修", "检修"), ("保养", "保养")], default="", string="实时状态")
online_time = fields.Char('开机时长', reaonly='True') online_time = fields.Char('开机时长')
offline_time = fields.Char('关机时长', reaonly='True') offline_time = fields.Char('关机时长')
offline_nums = fields.Integer('关机次数', reaonly='True') offline_nums = fields.Integer('关机次数')
# 待机时长 # 待机时长
idle_time = fields.Char('待机时长', reaonly='True') idle_time = fields.Char('待机时长')
# 待机率 # 待机率
idle_rate = fields.Char('待机率', reaonly='True') idle_rate = fields.Char('待机率')
work_time = fields.Char('加工时长', reaonly='True') work_time = fields.Char('加工时长')
work_rate = fields.Char('可用率', reaonly='True') work_rate = fields.Char('可用率')
fault_time = fields.Char('故障时长', reaonly='True') fault_time = fields.Char('故障时长')
fault_rate = fields.Char('故障率', reaonly='True') fault_rate = fields.Char('故障率')
fault_nums = fields.Integer('故障次数', reaonly='True') fault_nums = fields.Integer('故障次数')
detail_ids = fields.One2many('maintenance.equipment.oee.log.detail', 'log_id', string='日志详情') detail_ids = fields.One2many('maintenance.equipment.oee.log.detail', 'log_id', string='日志详情')
@@ -84,15 +81,12 @@ class SfMaintenanceEquipmentOEELog(models.Model):
class SfMaintenanceEquipmentOEELogDetail(models.Model): class SfMaintenanceEquipmentOEELogDetail(models.Model):
_name = 'maintenance.equipment.oee.log.detail' _name = 'maintenance.equipment.oee.log.detail'
_description = '设备运行日志详情' _description = '设备运行日志详情'
_order = 'time desc'
# sequence = fields.Integer('序号', related='id') sequence = fields.Integer('序号')
time = fields.Datetime('时间') time = fields.Datetime('时间')
state = fields.Selection([("加工", "加工"), ("关机", "关机"), ("待机", "待机"), ("故障", "故障"), state = fields.Selection([("加工", "加工"), ("关机", "关机"), ("待机", "待机"), ("故障", "故障"),
("检修", "检修"), ("保养", "保养")], default="", string="事件/状态") ("检修", "检修"), ("保养", "保养")], default="", string="事件/状态")
production_name = fields.Char('加工工单') production_id = fields.Many2one('mrp.production', '加工工单')
log_id = fields.Many2one('maintenance.equipment.oee.logs', '日志') log_id = fields.Many2one('maintenance.equipment.oee.logs', '日志')
# equipment_code = fields.Char('设备编码', related='log_id.equipment_code')
equipment_code = fields.Char('设备编码', readonly='True')

View File

@@ -159,8 +159,6 @@
<field name="equipment_id" domain="[('name','ilike','加工中心')]"/> <field name="equipment_id" domain="[('name','ilike','加工中心')]"/>
<field name="type_id"/> <field name="type_id"/>
<field name="state"/> <field name="state"/>
<field name="equipment_code"/>
<field name="function_type"/>
</group> </group>
</group> </group>
<group> <group>
@@ -204,10 +202,10 @@
<!-- <field name="detail_ids" domain="[('time','&lt;',(datetime.datetime.now() - datetime.timedelta(days=1)).strftime('%Y-%m-%d %H:%M:%S'))]"> --> <!-- <field name="detail_ids" domain="[('time','&lt;',(datetime.datetime.now() - datetime.timedelta(days=1)).strftime('%Y-%m-%d %H:%M:%S'))]"> -->
<field name="detail_ids" domain="[('state','ilike','加工')]"> <field name="detail_ids" domain="[('state','ilike','加工')]">
<tree> <tree>
<!-- <field name="sequence"/> --> <field name="sequence"/>
<field name="time"/> <field name="time"/>
<field name="state"/> <field name="state"/>
<field name="production_name"/> <field name="production_id"/>
</tree> </tree>
<!-- <form> --> <!-- <form> -->
<!-- <field name="sequence"/> --> <!-- <field name="sequence"/> -->
@@ -221,10 +219,10 @@
<page string="历史日志详情"> <page string="历史日志详情">
<field name="detail_ids"> <field name="detail_ids">
<tree> <tree>
<!-- <field name="sequence"/> --> <field name="sequence"/>
<field name="time"/> <field name="time"/>
<field name="state"/> <field name="state"/>
<field name="production_name"/> <field name="production_id"/>
</tree> </tree>
<!-- <form> --> <!-- <form> -->
<!-- <field name="sequence"/> --> <!-- <field name="sequence"/> -->
@@ -265,10 +263,10 @@
<field name="model">maintenance.equipment.oee.log.detail</field> <field name="model">maintenance.equipment.oee.log.detail</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree> <tree>
<!-- <field name="sequence"/> --> <field name="sequence"/>
<field name="time"/> <field name="time"/>
<field name="state"/> <field name="state"/>
<field name="production_name"/> <field name="production_id"/>
</tree> </tree>
</field> </field>
</record> </record>
@@ -282,10 +280,10 @@
<group> <group>
<group> <group>
<field name="state"/> <field name="state"/>
<!-- <field name="production_id"/> --> <field name="production_id"/>
</group> </group>
<group> <group>
<!-- <field name="sequence"/> --> <field name="sequence"/>
<field name="time"/> <field name="time"/>
</group> </group>
</group> </group>

View File

@@ -15,14 +15,12 @@
'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',
'data/agv_scheduling_data.xml',
'security/group_security.xml', 'security/group_security.xml',
'security/ir.model.access.csv', 'security/ir.model.access.csv',
'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',
'views/mrp_views_menus.xml', 'views/mrp_views_menus.xml',
'views/agv_scheduling_views.xml',
'views/stock_lot_views.xml', 'views/stock_lot_views.xml',
'views/mrp_production_addional_change.xml', 'views/mrp_production_addional_change.xml',
'views/mrp_routing_workcenter_view.xml', 'views/mrp_routing_workcenter_view.xml',
@@ -32,7 +30,7 @@
'views/model_type_view.xml', 'views/model_type_view.xml',
'views/agv_setting_views.xml', 'views/agv_setting_views.xml',
'views/sf_maintenance_equipment.xml', 'views/sf_maintenance_equipment.xml',
'views/res_config_settings_views.xml',
], ],
'assets': { 'assets': {
@@ -42,9 +40,7 @@
'web.assets_backend': [ 'web.assets_backend': [
'sf_manufacturing/static/src/xml/kanban_change.xml', 'sf_manufacturing/static/src/xml/kanban_change.xml',
'sf_manufacturing/static/src/js/kanban_change.js', 'sf_manufacturing/static/src/js/kanban_change.js',
'sf_manufacturing/static/src/scss/kanban_change.scss', 'sf_manufacturing/static/src/scss/kanban_change.scss'
'sf_manufacturing/static/src/xml/button_show_on_tree.xml',
'sf_manufacturing/static/src/js/workpiece_delivery_wizard_confirm.js',
] ]
}, },

View File

@@ -2,8 +2,6 @@
import logging import logging
import json import json
from datetime import datetime from datetime import datetime
from odoo.addons.sf_manufacturing.models.agv_scheduling import RepeatTaskException
from odoo import http from odoo import http
from odoo.http import request from odoo.http import request
@@ -147,7 +145,7 @@ class Manufacturing_Connect(http.Controller):
logging.info('get_qcCheck error:%s' % e) logging.info('get_qcCheck error:%s' % e)
return json.JSONEncoder().encode(res) return json.JSONEncoder().encode(res)
@http.route('/AutoDeviceApi/FeedBackStart', type='json', auth='none', methods=['GET', 'POST'], csrf=False, @http.route('/AutoDeviceApi/FeedBackStart', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
cors="*") cors="*")
def button_Work_START(self, **kw): def button_Work_START(self, **kw):
""" """
@@ -195,7 +193,7 @@ class Manufacturing_Connect(http.Controller):
logging.info('button_Work_START error:%s' % e) logging.info('button_Work_START error:%s' % e)
return json.JSONEncoder().encode(res) return json.JSONEncoder().encode(res)
@http.route('/AutoDeviceApi/FeedBackEnd', type='json', auth='none', methods=['GET', 'POST'], csrf=False, @http.route('/AutoDeviceApi/FeedBackEnd', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
cors="*") cors="*")
def button_Work_End(self, **kw): def button_Work_End(self, **kw):
""" """
@@ -246,7 +244,7 @@ class Manufacturing_Connect(http.Controller):
logging.info('button_Work_End error:%s' % e) logging.info('button_Work_End error:%s' % e)
return json.JSONEncoder().encode(res) return json.JSONEncoder().encode(res)
@http.route('/AutoDeviceApi/PartQualityInspect', type='json', auth='none', methods=['GET', 'POST'], csrf=False, @http.route('/AutoDeviceApi/PartQualityInspect', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
cors="*") cors="*")
def PartQualityInspect(self, **kw): def PartQualityInspect(self, **kw):
""" """
@@ -292,7 +290,7 @@ class Manufacturing_Connect(http.Controller):
logging.info('PartQualityInspect error:%s' % e) logging.info('PartQualityInspect error:%s' % e)
return json.JSONEncoder().encode(res) return json.JSONEncoder().encode(res)
@http.route('/AutoDeviceApi/CMMProgDolod', type='json', auth='none', methods=['GET', 'POST'], csrf=False, @http.route('/AutoDeviceApi/CMMProgDolod', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
cors="*") cors="*")
def CMMProgDolod(self, **kw): def CMMProgDolod(self, **kw):
""" """
@@ -332,7 +330,7 @@ class Manufacturing_Connect(http.Controller):
logging.info('CMMProgDolod error:%s' % e) logging.info('CMMProgDolod error:%s' % e)
return json.JSONEncoder().encode(res) return json.JSONEncoder().encode(res)
@http.route('/AutoDeviceApi/NCProgDolod', type='json', auth='none', methods=['GET', 'POST'], csrf=False, @http.route('/AutoDeviceApi/NCProgDolod', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
cors="*") cors="*")
def NCProgDolod(self, **kw): def NCProgDolod(self, **kw):
""" """
@@ -388,7 +386,7 @@ class Manufacturing_Connect(http.Controller):
ret = json.loads(datas) ret = json.loads(datas)
request.env['center_control.interface.log'].sudo().create( request.env['center_control.interface.log'].sudo().create(
{'content': ret, 'name': 'AutoDeviceApi/LocationChange'}) {'content': ret, 'name': 'AutoDeviceApi/LocationChange'})
logging.info('库位变更LocationChange_ret:%s' % ret) logging.info('LocationChange_ret===========:%s' % ret)
RfidCode = ret['RfidCode'] RfidCode = ret['RfidCode']
ChangeType = ret['ChangeType'] ChangeType = ret['ChangeType']
OldDeciveId = ret['OldDeciveId'] OldDeciveId = ret['OldDeciveId']
@@ -398,80 +396,34 @@ class Manufacturing_Connect(http.Controller):
OldDeciveStart = ret['OldDeciveStart'] OldDeciveStart = ret['OldDeciveStart']
OldDeciveEnd = ret['OldDeciveEnd'] OldDeciveEnd = ret['OldDeciveEnd']
if ChangeType == 'Part': temp_val_sn_id = None
temp_val_sn_id = None old_localtion = None
old_localtion = None # if ChangeType == 'Part' or ChangeType == 'Tool':
# if ChangeType == 'Part' or ChangeType == 'Tool': stock_lot_obj = request.env['stock.lot'].sudo().search(
stock_lot_obj = request.env['stock.lot'].sudo().search( [('rfid', '=', RfidCode)], limit=1)
[('rfid', '=', RfidCode)], limit=1) logging.info('stock_lot_obj===========:%s' % stock_lot_obj)
logging.info('stock_lot_obj===========:%s' % stock_lot_obj) if not stock_lot_obj:
if not stock_lot_obj: res = {'Succeed': False, 'ErrorCode': 202, 'Error': '未根据RfidCode找到该产品'}
res = {'Succeed': False, 'ErrorCode': 202, 'Error': '未根据RfidCode找到该产品'} return json.JSONEncoder().encode(res)
return json.JSONEncoder().encode(res) if OldPosition:
if OldPosition: old_localtion = request.env['sf.shelf.location'].sudo().search(
old_localtion = request.env['sf.shelf.location'].sudo().search( [('barcode', '=', OldPosition)], limit=1)
[('barcode', '=', OldPosition)], limit=1) logging.info('old_localtion===========:%s' % old_localtion)
logging.info('old_localtion===========:%s' % old_localtion) new_localtion = request.env['sf.shelf.location'].sudo().search(
new_localtion = request.env['sf.shelf.location'].sudo().search( [('barcode', '=', NewPosition)], limit=1)
[('barcode', '=', NewPosition)], limit=1) logging.info('new_localtion===========:%s' % new_localtion)
logging.info('new_localtion===========:%s' % new_localtion) if not new_localtion:
if not new_localtion: res = {'Succeed': False, 'ErrorCode': 202, 'Error': '没有该目标位置'}
res = {'Succeed': False, 'ErrorCode': 202, 'Error': '没有该目标位置'} return json.JSONEncoder().encode(res)
return json.JSONEncoder().encode(res) if old_localtion:
if old_localtion: temp_val_sn_id = old_localtion.product_sn_id
temp_val_sn_id = old_localtion.product_sn_id logging.info('temp_val_sn_id===========:%s' % temp_val_sn_id)
logging.info('temp_val_sn_id===========:%s' % temp_val_sn_id) old_localtion.product_sn_id = None
old_localtion.product_sn_id = None new_localtion.product_sn_id = temp_val_sn_id
new_localtion.product_sn_id = temp_val_sn_id logging.info('====1======')
logging.info('====1======') else:
else: new_localtion.product_sn_id = stock_lot_obj.id
new_localtion.product_sn_id = stock_lot_obj.id logging.info('=====2======')
logging.info('=====2======')
elif ChangeType == 'Tool':
# 对功能刀具库位变更信息进行更改
def write_tool(DeciveId):
if 'Tool' in DeciveId:
shelfinfo = list(filter(lambda x: x.get('DeviceId') == DeciveId,
request.env['sf.shelf.location'].sudo().get_sf_shelf_location_info(
DeciveId)))
total_data = request.env['sf.shelf.location.datasync'].sudo().get_total_data()
for item in shelfinfo:
logging.info('货架已获取信息:%s' % item)
shelf_barcode = request.env['sf.shelf.location.datasync'].sudo().find_our_code(
total_data, item['Postion'])
location_id = request.env['sf.shelf.location'].sudo().search(
[('barcode', '=', shelf_barcode)],
limit=1)
if location_id:
# 如果是线边刀库信息,则对功能刀具移动生成记录
if 'Tool' in item['Postion']:
tool = request.env['sf.functional.cutting.tool.entity'].sudo().search(
[('rfid', '=', item['RfidCode']), ('functional_tool_status', '!=', '已拆除')])
tool.sudo().tool_in_out_stock_location(location_id)
if tool:
location_id.product_sn_id = tool.barcode_id.id
# 修改功能刀具状态
if item.get('State') == '报警':
if tool.functional_tool_status != item.get('State'):
tool.write({
'functional_tool_status': item['State']
})
else:
location_id.product_sn_id = False
if item['RfidCode']:
logging.info('Rfid为【%s】的功能刀具在系统中不存在!' % item['RfidCode'])
else:
equipment_id = request.env['maintenance.equipment'].sudo().search([('name', '=', DeciveId)])
if equipment_id:
equipment_id.sudo().register_equipment_tool()
else:
res_1 = {'Succeed': False, 'ErrorCode': 202, 'Error': f'设备【{DeciveId}】不存在'}
return json.JSONEncoder().encode(res_1)
if OldDeciveId:
write_tool(OldDeciveId)
elif NewDeciveId:
write_tool(NewDeciveId)
except Exception as e: except Exception as e:
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e} res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
logging.info('LocationChange error:%s' % e) logging.info('LocationChange error:%s' % e)
@@ -496,16 +448,13 @@ class Manufacturing_Connect(http.Controller):
if 'DeviceId' in ret: if 'DeviceId' in ret:
logging.info('DeviceId:%s' % ret['DeviceId']) logging.info('DeviceId:%s' % ret['DeviceId'])
if 'IsComplete' in ret: if 'IsComplete' in ret:
rfid_codes = []
workorder_ids = []
if ret['IsComplete'] is True or ret['IsComplete'] is False: if ret['IsComplete'] is True or ret['IsComplete'] is False:
for i in range(1, 5): for i in range(1, 5):
logging.info('F-RfidCode:%s' % i) logging.info('F-RfidCode:%s' % i)
if f'RfidCode{i}' in ret: if f'RfidCode{i}' in ret:
rfid_code = ret[f'RfidCode{i}'] rfid_code = ret[f'RfidCode{i}']
logging.info('RfidCode:%s' % rfid_code) logging.info('RfidCode:%s' % rfid_code)
if rfid_code is not None and rfid_code != '': if rfid_code is not None:
rfid_codes.append(rfid_code)
domain = [ domain = [
('rfid_code', '=', rfid_code), ('rfid_code', '=', rfid_code),
('routing_type', '=', 'CNC加工'), ('state', '!=', 'rework') ('routing_type', '=', 'CNC加工'), ('state', '!=', 'rework')
@@ -513,7 +462,6 @@ class Manufacturing_Connect(http.Controller):
workorder = request.env['mrp.workorder'].sudo().search(domain, order='id asc') workorder = request.env['mrp.workorder'].sudo().search(domain, order='id asc')
if workorder: if workorder:
for order in workorder: for order in workorder:
workorder_ids.append(order.id)
if order.production_line_state == '待上产线': if order.production_line_state == '待上产线':
logging.info( logging.info(
'工单产线状态:%s' % order.production_line_state) '工单产线状态:%s' % order.production_line_state)
@@ -522,30 +470,23 @@ class Manufacturing_Connect(http.Controller):
('processing_panel', '=', order.processing_panel)]) ('processing_panel', '=', order.processing_panel)])
if panel_workorder: if panel_workorder:
panel_workorder.write({'production_line_state': '已上产线'}) panel_workorder.write({'production_line_state': '已上产线'})
# workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search( workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search(
# [ [
# ('rfid_code', '=', rfid_code), ('type', '=', '上产线'), ('rfid_code', '=', rfid_code), ('type', '=', '上产线'),
# ('production_id', '=', order.production_id.id), ('production_id', '=', order.production_id.id),
# ('workorder_id', '=', order.id), ('workorder_id', '=', order.id),
# ('workorder_state', '=', 'done')]) ('workorder_state', '=', 'done')])
# if workpiece_delivery.status == '待下发': if workpiece_delivery.status == '待下发':
# workpiece_delivery.write({'is_manual_work': True}) workpiece_delivery.write({'is_manual_work': True})
# 下发
else: else:
res = {'Succeed': False, 'ErrorCode': 204, res = {'Succeed': False, 'ErrorCode': 204,
'Error': 'DeviceId为%s没有对应的已配送工件数据' % ret['DeviceId']} 'Error': 'DeviceId为%s没有对应的已配送工件数据' % ret['DeviceId']}
if ret['IsComplete'] is True:
# 向AGV任务调度下发运送空料架任务
workorders = request.env['mrp.workorder'].browse(workorder_ids)
request.env['sf.agv.scheduling'].add_scheduling(ret['DeviceId'], '运送空料架', workorders)
else: else:
res = {'Succeed': False, 'ErrorCode': 203, 'Error': '未传IsComplete字段'} res = {'Succeed': False, 'ErrorCode': 203, 'Error': '未传IsComplete字段'}
else: else:
res = {'Succeed': False, 'ErrorCode': 201, 'Error': '未传DeviceId字段'} res = {'Succeed': False, 'ErrorCode': 201, 'Error': '未传DeviceId字段'}
except RepeatTaskException as e:
logging.info('AGVToProduct error:%s' % e)
except Exception as e: except Exception as e:
res = {'Succeed': False, 'ErrorCode': 202, 'Error': str(e)} res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
logging.info('AGVToProduct error:%s' % e) logging.info('AGVToProduct error:%s' % e)
return json.JSONEncoder().encode(res) return json.JSONEncoder().encode(res)
@@ -568,8 +509,7 @@ class Manufacturing_Connect(http.Controller):
logging.info('ret:%s' % ret) logging.info('ret:%s' % ret)
if 'DeviceId' in ret: if 'DeviceId' in ret:
logging.info('DeviceId:%s' % ret['DeviceId']) logging.info('DeviceId:%s' % ret['DeviceId'])
# delivery_Arr = [] delivery_Arr = []
workorder_ids = []
if 'IsComplete' in ret: if 'IsComplete' in ret:
if ret['IsComplete'] is True or ret['IsComplete'] is False: if ret['IsComplete'] is True or ret['IsComplete'] is False:
for i in range(1, 5): for i in range(1, 5):
@@ -577,7 +517,7 @@ class Manufacturing_Connect(http.Controller):
if f'RfidCode{i}' in ret: if f'RfidCode{i}' in ret:
rfid_code = ret[f'RfidCode{i}'] rfid_code = ret[f'RfidCode{i}']
logging.info('RfidCode:%s' % rfid_code) logging.info('RfidCode:%s' % rfid_code)
if rfid_code is not None and rfid_code != '': if rfid_code is not None:
domain = [ domain = [
('rfid_code', '=', rfid_code), ('rfid_code', '=', rfid_code),
('routing_type', '=', 'CNC加工'), ('state', '!=', 'rework') ('routing_type', '=', 'CNC加工'), ('state', '!=', 'rework')
@@ -585,7 +525,6 @@ class Manufacturing_Connect(http.Controller):
workorder = request.env['mrp.workorder'].sudo().search(domain, order='id asc') workorder = request.env['mrp.workorder'].sudo().search(domain, order='id asc')
if workorder: if workorder:
for order in workorder: for order in workorder:
workorder_ids.append(order.id)
if order.production_line_state == '已上产线': if order.production_line_state == '已上产线':
logging.info( logging.info(
'工单产线状态:%s' % order.production_line_state) '工单产线状态:%s' % order.production_line_state)
@@ -595,41 +534,35 @@ class Manufacturing_Connect(http.Controller):
if panel_workorder: if panel_workorder:
panel_workorder.write({'production_line_state': '已下产线'}) panel_workorder.write({'production_line_state': '已下产线'})
workorder.write({'state': 'to be detected'}) workorder.write({'state': 'to be detected'})
# workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search( workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search(
# [ [
# ('rfid_code', '=', rfid_code), ('type', '=', '下产线'), ('rfid_code', '=', rfid_code), ('type', '=', '下产线'),
# ('production_id', '=', order.production_id.id), ('production_id', '=', order.production_id.id),
# ('workorder_id', '=', order.id), ('workorder_id', '=', order.id),
# ('workorder_state', '=', 'done')]) ('workorder_state', '=', 'done')])
# if workpiece_delivery: if workpiece_delivery:
# delivery_Arr.append(workpiece_delivery.id) delivery_Arr.append(workpiece_delivery.id)
else: else:
res = {'Succeed': False, 'ErrorCode': 204, res = {'Succeed': False, 'ErrorCode': 204,
'Error': 'DeviceId为%s没有对应的已配送工件数据' % ret['DeviceId']} 'Error': 'DeviceId为%s没有对应的已配送工件数据' % ret['DeviceId']}
# if delivery_Arr: if delivery_Arr:
# logging.info('delivery_Arr:%s' % delivery_Arr) logging.info('delivery_Arr:%s' % delivery_Arr)
# delivery_workpiece = request.env['sf.workpiece.delivery'].sudo().search( delivery_workpiece = request.env['sf.workpiece.delivery'].sudo().search(
# [('id', 'in', delivery_Arr)]) [('id', 'in', delivery_Arr)])
# if delivery_workpiece: if delivery_workpiece:
# logging.info('开始向agv下发下产线任务') logging.info('开始向agv下发下产线任务')
# agv_site = request.env['sf.agv.site'].sudo().search([]) agv_site = request.env['sf.agv.site'].sudo().search([])
# if agv_site: if agv_site:
# has_site = agv_site.update_site_state() has_site = agv_site.update_site_state()
# if has_site is True: if has_site is True:
# is_free = delivery_workpiece._check_avgsite_state() is_free = delivery_workpiece._check_avgsite_state()
# if is_free is True: if is_free is True:
# delivery_workpiece._delivery_avg() delivery_workpiece._delivery_avg()
# logging.info('agv下发下产线任务下发完成') logging.info('agv下发下产线任务下发完成')
if ret['IsComplete'] is True:
# 向AGV任务调度下发下产线任务
workorders = request.env['mrp.workorder'].browse(workorder_ids)
request.env['sf.agv.scheduling'].add_scheduling(ret['DeviceId'], '下产线', workorders)
else: else:
res = {'Succeed': False, 'ErrorCode': 203, 'Error': '未传IsComplete字段'} res = {'Succeed': False, 'ErrorCode': 203, 'Error': '未传IsComplete字段'}
except RepeatTaskException as e:
logging.info('AGVToProduct error:%s' % e)
except Exception as e: except Exception as e:
res = {'Succeed': False, 'ErrorCode': 202, 'Error': str(e)} res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
logging.info('AGVDownProduct error:%s' % e) logging.info('AGVDownProduct error:%s' % e)
return json.JSONEncoder().encode(res) return json.JSONEncoder().encode(res)
@@ -667,32 +600,3 @@ class Manufacturing_Connect(http.Controller):
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e} res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
logging.info('AGVDownProduct error:%s' % e) logging.info('AGVDownProduct error:%s' % e)
return json.JSONEncoder().encode(res) return json.JSONEncoder().encode(res)
@http.route('/AutoDeviceApi/AgvStationState', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
cors="*")
def AGVStationState(self, **kw):
"""
中控推送接驳站状态
:param kw:
:return:
"""
logging.info('AGVStationState:%s' % kw)
try:
res = {'Succeed': True}
datas = request.httprequest.data
ret = json.loads(datas)
request.env['center_control.interface.log'].sudo().create(
{'content': ret, 'name': 'AutoDeviceApi/AGVStationState'})
logging.info('ret:%s' % ret)
ret = ret['param']
params = {}
for i in range(len(ret)):
if 'DeviceId' in ret[i] and 'AtHome' in ret[i]:
logging.info('DeviceId:%s, AtHome:%s' % (ret[i]['DeviceId'], ret[i]['AtHome']))
params[ret[i]['DeviceId']] = '占用' if ret[i]['AtHome'] else '空闲'
if params:
request.env['sf.agv.site'].update_site_state(params)
except Exception as e:
res = {'Succeed': False, 'ErrorCode': 202, 'Error': str(e)}
logging.info('AGVDownProduct error:%s' % e)
return json.JSONEncoder().encode(res)

View File

@@ -8,7 +8,7 @@ from odoo.http import request
class Workpiece(http.Controller): class Workpiece(http.Controller):
@http.route('/agvApi/backfeed', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False, @http.route('/agvApi/backfeed', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
cors="*") cors="*")
def backfeed(self, **kw): def backfeed(self, **kw):
""" """
@@ -25,14 +25,15 @@ class Workpiece(http.Controller):
if 'reqCode' in ret: if 'reqCode' in ret:
if 'method' in ret: if 'method' in ret:
if ret['method'] == 'end': if ret['method'] == 'end':
# 找到对应的AGV调度任务 req_codes = ret['reqCode'].split(',')
agv_scheduling = request.env['sf.agv.scheduling'].sudo().search( for req_code in req_codes:
[('name', '=', ret['reqCode']), ('state', '=', '配送中')]) workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search(
if agv_scheduling: [('name', '=', req_code.strip()), ('task_completion_time', '=', False)])
agv_scheduling.finish_scheduling() if workpiece_delivery:
else: workpiece_delivery.write({'status': '已配送', 'task_completion_time': datetime.now()})
res = {'Succeed': False, 'ErrorCode': 203, else:
'Error': '该reqCode暂未查到对应的AGV任务记录'} res = {'Succeed': False, 'ErrorCode': 203,
'Error': '该reqCode暂未查到对应的工件配送记录'}
else: else:
res = {'Succeed': False, 'ErrorCode': 204, 'Error': '未传method字段'} res = {'Succeed': False, 'ErrorCode': 204, 'Error': '未传method字段'}
else: else:

View File

@@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="0">
<record id="sequence_agv_scheduling" model="ir.sequence">
<field name="name">AGV调度</field>
<field name="code">sf.agv.scheduling</field>
<field name="prefix">B%(year)s%(month)s%(day)s</field>
<field name="padding">4</field>
<field name="number_next">1</field>
<field name="implementation">standard</field>
<field name="use_date_range">True</field>
<field name="date_range_period">day</field>
<field name="company_id" eval="False"/>
</record>
</data>
</odoo>

View File

@@ -9,5 +9,3 @@ from . import stock
from . import res_user from . import res_user
from . import production_line_base from . import production_line_base
from . import agv_setting from . import agv_setting
from . import agv_scheduling
from . import res_config_setting

View File

@@ -1,267 +0,0 @@
import requests
from odoo import models, fields, api, _
from odoo.exceptions import UserError
import logging
_logger = logging.getLogger(__name__)
class RepeatTaskException(UserError):
pass
class AgvScheduling(models.Model):
_name = 'sf.agv.scheduling'
_description = 'agv调度'
_order = 'id desc'
name = fields.Char('任务单号', index=True, copy=False)
def _get_agv_route_type_selection(self):
return self.env['sf.agv.task.route'].fields_get(['route_type'])['route_type']['selection']
agv_route_type = fields.Selection(selection=_get_agv_route_type_selection, string='任务类型', required=True)
agv_route_id = fields.Many2one('sf.agv.task.route', '任务路线')
start_site_id = fields.Many2one('sf.agv.site', '起点接驳站', required=True)
end_site_id = fields.Many2one('sf.agv.site', '终点接驳站', tracking=True)
site_state = fields.Selection([
('占用', '占用'),
('空闲', '空闲')], string='终点接驳站状态', default='占用')
state = fields.Selection([
('待下发', '待下发'),
('配送中', '配送中'),
('已配送', '已配送'),
('已取消', '已取消')], string='状态', default='待下发', tracking=True)
workorder_ids = fields.Many2many('mrp.workorder', 'sf_agv_scheduling_mrp_workorder_ref', string='关联工单')
task_create_time = fields.Datetime('任务创建时间')
task_delivery_time = fields.Datetime('任务下发时间')
task_completion_time = fields.Datetime('任务完成时间')
task_duration = fields.Char('任务时长', compute='_compute_task_duration')
@api.depends('agv_route_type')
def _compute_delivery_workpieces(self):
for record in self:
if record.agv_route_type == '运送空料架':
record.delivery_workpieces = '/'
else:
record.delivery_workpieces = ''.join(record.workorder_ids.mapped('production_id.name'))
delivery_workpieces = fields.Char('配送工件', compute=_compute_delivery_workpieces)
@api.model
def web_search_read(self, domain=None, fields=None, offset=0, limit=None, order=None, count_limit=None):
domain = domain or []
new_domain = []
for index, item in enumerate(domain):
if isinstance(item, list):
if item[0] == 'delivery_workpieces':
new_domain.append('&')
new_domain.append(['workorder_ids.production_id.name', item[1], item[2]])
new_domain.append(['agv_route_type', '!=', '运送空料架'])
continue
new_domain.append(item)
return super(AgvScheduling, self).web_search_read(new_domain, fields, limit=limit, offset=offset)
@api.depends('task_completion_time', 'task_delivery_time')
def _compute_task_duration(self):
for rec in self:
if rec.task_completion_time and rec.task_delivery_time:
rec.task_duration = str(rec.task_completion_time - rec.task_delivery_time)
else:
rec.task_duration = ''
@api.model_create_multi
def create(self, vals_list):
# We generate a standard reference
for vals in vals_list:
vals['name'] = self.env['ir.sequence'].next_by_code('sf.agv.scheduling') or _('New')
return super().create(vals_list)
def add_scheduling(self, agv_start_site_name, agv_route_type, workorders):
""" add_scheduling(agv_start_site_id, agv_route_type, workorders) -> agv_scheduling
新增AGV调度
params:
agv_start_site_name: AGV起点接驳站名称
agv_route_type: AGV任务类型
workorders: 工单
"""
_logger.info('创建AGV调度任务\r\n起点为【%s】,任务类型为【%s】,工单为【%s' % (agv_start_site_name, agv_route_type, workorders))
if not workorders:
raise UserError(_('工单不能为空'))
agv_start_site = self.env['sf.agv.site'].sudo().search([('name', '=', agv_start_site_name)], limit=1)
if not agv_start_site:
raise UserError(_('不存在名称为【%s】的接驳站,请先创建!' % agv_start_site_name))
# 如果存在相同任务类型工单的AGV调度任务则提示错误
agv_scheduling = self.sudo().search([
('workorder_ids', 'in', workorders.ids),
('agv_route_type', '=', agv_route_type),
('state', 'in', ['待下发', '配送中'])
], limit=1)
if agv_scheduling:
# 计算agv_scheduling.workorder_ids与workorders的交集
repetitive_workorders = agv_scheduling.workorder_ids & workorders
raise RepeatTaskException(
'制造订单号【%s】已存在于【%s】AGV调度任务请勿重复下发' %
(','.join(repetitive_workorders.mapped('production_id.name')), agv_scheduling.name)
)
vals = {
'start_site_id': agv_start_site.id,
'agv_route_type': agv_route_type,
'workorder_ids': workorders.ids,
# 'workpiece_delivery_ids': deliveries.mapped('id') if deliveries else [],
'task_create_time': fields.Datetime.now()
}
# 如果只有唯一任务路线,则自动赋予终点接驳站跟任务名称
agv_routes = self.env['sf.agv.task.route'].sudo().search([
('route_type', '=', agv_route_type),
('start_site_id', '=', agv_start_site.id)
])
if not agv_routes:
raise UserError(_('不存在起点为【%s】的【%s】任务路线,请先创建!' % (agv_start_site_name, agv_route_type)))
idle_route = None
if len(agv_routes) == 1:
idle_route = agv_routes[0]
vals.update({'end_site_id': idle_route.end_site_id.id, 'agv_route_id': idle_route.id})
else:
# 判断终点接驳站是否为空闲
idle_routes = agv_routes.filtered(lambda r: r.end_site_id.state == '空闲')
if idle_routes:
# 将空闲的路线按照终点接驳站名称排序
idle_routes = sorted(idle_routes, key=lambda r: r.end_site_id.name)
idle_route = idle_routes[0]
vals.update({'end_site_id': idle_route.end_site_id.id, 'agv_route_id': idle_route.id})
try:
scheduling = self.env['sf.agv.scheduling'].sudo().create(vals)
# 触发空闲接驳站状态更新,触发新任务下发
if idle_route and idle_route.end_site_id.state == '空闲':
scheduling.dispatch_scheduling(idle_route)
except Exception as e:
_logger.error('添加AGV调度任务失败: %s', e)
raise UserError(_('添加AGV调度任务失败: %s', e))
return scheduling
def on_site_state_change(self, agv_site_id, agv_site_state):
"""
响应AGV接驳站站点状态变化
params:
agv_site_id: 接驳站ID
agv_site_state: 站点状态('空闲', '占用'
"""
if agv_site_state == '空闲':
# 查询终点接驳站为agv_site_id的AGV路线
task_routes = self.env['sf.agv.task.route'].sudo().search([('end_site_id', '=', agv_site_id)])
agv_scheduling = self.env['sf.agv.scheduling'].sudo().search(
[('state', '=', '待下发'), ('agv_route_type', 'in', task_routes.mapped('route_type'))],
order='id asc',
limit=1
)
task_route = task_routes.filtered(
lambda r: r.start_site_id == agv_scheduling.start_site_id and r.start_site_id == agv_scheduling.start_site_id
)
if task_route:
# 下发AGV调度任务并修改接驳站状态为占用
agv_scheduling.dispatch_scheduling(task_route)
def _delivery_avg(self):
config = self.env['res.config.settings'].get_values()
position_code_arr = [{
'positionCode': self.start_site_id.name,
'code': '00'
}, {
'positionCode': self.end_site_id.name,
'code': '00'
}]
res = {'reqCode': self.name, 'reqTime': '', 'clientCode': '', 'tokenCode': '',
'taskTyp': 'F01', 'ctnrTyp': '', 'ctnrCode': '', 'wbCode': config['wbcode'],
'positionCodePath': position_code_arr,
'podCode': '',
'podDir': '', 'materialLot': '', 'priority': '', 'taskCode': '', 'agvCode': '', 'materialLot': '',
'data': ''}
try:
logging.info('AGV请求路径:%s' % config['agv_rcs_url'])
logging.info('AGV-json:%s' % res)
headers = {'Content-Type': 'application/json'}
ret = requests.post((config['agv_rcs_url']), json=res, headers=headers)
ret = ret.json()
logging.info('config-ret:%s' % ret)
if ret['code'] == 0:
return True
else:
raise UserError(ret['message'])
except Exception as e:
logging.info('config-e:%s' % e)
raise UserError("工件配送请求agv失败:%s" % e)
def button_cancel(self):
# 弹出二次确认窗口后执行
for rec in self:
if rec.state != '待下发':
raise UserError('只有待下发状态的AGV调度任务才能取消')
rec.state = '已取消'
def finish_scheduling(self):
"""
完成调度任务
"""
for rec in self:
if rec.state != '配送中':
return False
_logger.info('AGV任务调度完成任务%s' % rec)
rec.state = '已配送'
rec.task_completion_time = fields.Datetime.now()
def dispatch_scheduling(self, agv_task_route):
"""
下发调度任务
params:
agv_route sf.agv.task.route对象
"""
for rec in self:
if rec.state != '待下发':
return False
_logger.info('AGV任务调度下发调度任务路线为%s' % agv_task_route)
rec.state = '配送中'
rec.task_delivery_time = fields.Datetime.now()
rec.site_state = '空闲'
rec.end_site_id = agv_task_route.end_site_id.id
rec.agv_route_id = agv_task_route.id
is_agv_task_dispatch = self.env['ir.config_parameter'].sudo().get_param('is_agv_task_dispatch')
if is_agv_task_dispatch:
rec._delivery_avg()
# 更新接驳站状态
rec.env['sf.agv.site'].update_site_state({rec.end_site_id.name: '占用'}, False)
def write(self, vals):
if vals.get('state', False):
if vals['state'] == '已取消':
self.env['sf.workpiece.delivery'].search([('agv_scheduling_id', '=', self.id)]).write({'status': '待下发'})
elif vals['state'] == '已配送':
self.env['sf.workpiece.delivery'].search([('agv_scheduling_id', '=', self.id)]).write({
'status': '已配送',
'feeder_station_destination_id': self.end_site_id.id,
'route_id': self.agv_route_id.id,
'task_completion_time': fields.Datetime.now()
})
elif vals['state'] == '配送中':
self.env['sf.workpiece.delivery'].search([('agv_scheduling_id', '=', self.id)]).write({
'feeder_station_destination_id': self.end_site_id.id,
'route_id': self.agv_route_id.id,
'task_delivery_time': fields.Datetime.now()
})
return super().write(vals)
class ResMrpWorkOrder(models.Model):
_inherit = 'mrp.workorder'
agv_scheduling_ids = fields.Many2many(
'sf.agv.scheduling',
'sf_agv_scheduling_mrp_workorder_ref',
string='AGV调度',
domain=[('state', '!=', '已取消')])

View File

@@ -5,77 +5,50 @@ import time
from odoo import fields, models, api from odoo import fields, models, api
from odoo.exceptions import UserError from odoo.exceptions import UserError
_logger = logging.getLogger(__name__)
class AgvSetting(models.Model): class AgvSetting(models.Model):
_name = 'sf.agv.site' _name = 'sf.agv.site'
_description = 'agv站点' _description = 'agv站点'
name = fields.Char('位置编号') name = fields.Char('位置编号')
# owning_region = fields.Char('所属区域') owning_region = fields.Char('所属区域')
state = fields.Selection([ state = fields.Selection([
('占用', '占用'), ('占用', '占用'),
('空闲', '空闲')], string='状态') ('空闲', '空闲')], string='状态')
divide_the_work = fields.Char('主要分工') divide_the_work = fields.Char('主要分工')
active = fields.Boolean('有效', default=True) active = fields.Boolean('有效', default=True)
workcenter_id = fields.Many2one(string='所属区域', comodel_name='mrp.workcenter', tracking=True,
domain=[('is_agv_scheduling', '=', True)])
# name必须唯一 def update_site_state(self):
_sql_constraints = [ # 调取中控的接驳站接口并修改对应站点的状态
('name_uniq', 'unique (name)', '站点编号必须唯一!'), config = self.env['res.config.settings'].get_values()
] # token = sf_sync_config['token'Ba F2CF5DCC-1A00-4234-9E95-65603F70CC8A]
headers = {'Authorization': config['center_control_Authorization']}
# def update_site_state(self): center_control_url = config['center_control_url'] + "/AutoDeviceApi/GetAgvStationState?date="
# # 调取中控的接驳站接口并修改对应站点的状态 timestamp = int(time.time())
# config = self.env['res.config.settings'].get_values() center_control_url += str(timestamp)
# # token = sf_sync_config['token'Ba F2CF5DCC-1A00-4234-9E95-65603F70CC8A] logging.info('工件配送-请求中控地址:%s' % center_control_url)
# headers = {'Authorization': config['center_control_Authorization']} try:
# center_control_url = config['center_control_url'] + "/AutoDeviceApi/GetAgvStationState?date=" center_control_r = requests.get(center_control_url, headers=headers, timeout=10) # 设置超时为60秒
# timestamp = int(time.time()) ret = center_control_r.json()
# center_control_url += str(timestamp) logging.info('工件配送-请求中控站点信息:%s' % ret)
# logging.info('工件配送-请求中控地址:%s' % center_control_url) self.env['center_control.interface.log'].sudo().create(
# try: {'content': ret, 'name': 'AutoDeviceApi/GetAgvStationState?date=%s' % str(timestamp)})
# center_control_r = requests.get(center_control_url, headers=headers, timeout=10) # 设置超时为60秒 if ret['Succeed'] is True:
# ret = center_control_r.json() datas = ret['Datas']
# logging.info('工件配送-请求中控站点信息:%s' % ret) for item in self:
# self.env['center_control.interface.log'].sudo().create( for da in datas:
# {'content': ret, 'name': 'AutoDeviceApi/GetAgvStationState?date=%s' % str(timestamp)}) if da['DeviceId'] == item.name:
# if ret['Succeed'] is True: if da['AtHome'] is True:
# datas = ret['Datas'] item.state = '占用'
# for item in self: else:
# for da in datas: item.state = '空闲'
# if da['DeviceId'] == item.name: return True
# if da['AtHome'] is True: except requests.exceptions.Timeout:
# item.state = '占用' logging.error('工件配送-请求中控接口超时')
# else: return False
# item.state = '空闲' except requests.exceptions.RequestException as e:
# return True logging.error('工件配送-请求中控接口错误: %s', e)
# except requests.exceptions.Timeout: return False
# logging.error('工件配送-请求中控接口超时')
# return False
# except requests.exceptions.RequestException as e:
# logging.error('工件配送-请求中控接口错误: %s', e)
# return False
def update_site_state(self, agv_site_state_arr, notify=True):
"""
更新接驳站状态
params:
agv_site_state_arr: {'A01': '空闲', 'B01': '占用'}
notify: 是否通知调度(非中控发起的状态改变不触发调度任务)
"""
if isinstance(agv_site_state_arr, dict):
for agv_site_name, is_occupy in agv_site_state_arr.items():
agv_site = self.env['sf.agv.site'].sudo().search([('name', '=', agv_site_name)])
if agv_site:
agv_site.state = is_occupy
if notify:
self.env['sf.agv.scheduling'].on_site_state_change(agv_site.id, agv_site.state)
else:
_logger.error("更新失败:接驳站站点错误!%s" % agv_site_name)
raise UserError("更新失败:接驳站站点错误!")
class AgvTaskRoute(models.Model): class AgvTaskRoute(models.Model):
@@ -98,17 +71,6 @@ class AgvTaskRoute(models.Model):
if self.end_site_id == self.start_site_id: if self.end_site_id == self.start_site_id:
raise UserError("您选择的终点接驳站与起点接驳站重复,请重新选择") raise UserError("您选择的终点接驳站与起点接驳站重复,请重新选择")
workcenter_id = fields.Many2one(string='所属区域', comodel_name='mrp.workcenter', domain=[('is_agv_scheduling', '=', True)],
compute="_compute_region")
@api.depends('end_site_id')
def _compute_region(self):
for record in self:
if record.end_site_id:
record.workcenter_id = record.end_site_id.workcenter_id
else:
record.workcenter_id = None
class Center_controlInterfaceLog(models.Model): class Center_controlInterfaceLog(models.Model):
_name = 'center_control.interface.log' _name = 'center_control.interface.log'

View File

@@ -1,14 +1,12 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import base64 import base64
import datetime
import logging import logging
import json import json
import os import os
import re import re
import requests import requests
from itertools import groupby from itertools import groupby
from collections import defaultdict, namedtuple from odoo import api, fields, models, _
from odoo import api, fields, models, SUPERUSER_ID, _
from odoo.exceptions import UserError, ValidationError from odoo.exceptions import UserError, ValidationError
from odoo.addons.sf_base.commons.common import Common from odoo.addons.sf_base.commons.common import Common
from odoo.tools import float_compare, float_round, float_is_zero, format_datetime from odoo.tools import float_compare, float_round, float_is_zero, format_datetime
@@ -79,10 +77,9 @@ class MrpProduction(models.Model):
('pending_cam', '待加工'), ('pending_cam', '待加工'),
('progress', '加工中'), ('progress', '加工中'),
('rework', '返工'), ('rework', '返工'),
('scrap', '报废'),
('to_close', 'To Close'), ('to_close', 'To Close'),
('done', 'Done'), ('done', 'Done'),
('cancel', '已取消')], string='State', ('cancel', '报废')], string='State',
compute='_compute_state', copy=False, index=True, readonly=True, compute='_compute_state', copy=False, index=True, readonly=True,
store=True, tracking=True, store=True, tracking=True,
help=" * Draft: The MO is not confirmed yet.\n" help=" * Draft: The MO is not confirmed yet.\n"
@@ -123,39 +120,8 @@ class MrpProduction(models.Model):
# 上传零件图纸 # 上传零件图纸
part_drawing = fields.Binary('零件图纸') part_drawing = fields.Binary('零件图纸')
@api.depends('product_id.manual_quotation') manual_quotation = fields.Boolean('人工编程', default=False, readonly=True)
def _compute_manual_quotation(self):
for item in self:
item.manual_quotation = item.product_id.manual_quotation
manual_quotation = fields.Boolean('人工编程', default=False, compute=_compute_manual_quotation, store=True)
is_scrap = fields.Boolean('是否报废', default=False) is_scrap = fields.Boolean('是否报废', default=False)
is_remanufacture = fields.Boolean('是否重新制造', default=False)
remanufacture_count = fields.Integer("重新制造订单数量", compute='_compute_remanufacture_production_ids')
remanufacture_production_id = fields.Many2one('mrp.production', string='')
@api.depends('remanufacture_production_id')
def _compute_remanufacture_production_ids(self):
for production in self:
if production.remanufacture_production_id:
remanufacture_production = self.env['mrp.production'].search(
[('id', '=', production.remanufacture_production_id.id)])
if remanufacture_production:
production.remanufacture_count = len(remanufacture_production)
else:
production.remanufacture_count = 0
def action_view_remanufacture_productions(self):
self.ensure_one()
mrp_production = self.env['mrp.production'].search(
[('id', '=', self.remanufacture_production_id.id)])
action = {
'res_model': 'mrp.production',
'type': 'ir.actions.act_window',
'view_mode': 'form',
'res_id': mrp_production.id,
}
return action
@api.depends( @api.depends(
'move_raw_ids.state', 'move_raw_ids.quantity_done', 'move_finished_ids.state', 'tool_state', 'move_raw_ids.state', 'move_raw_ids.quantity_done', 'move_finished_ids.state', 'tool_state',
@@ -200,7 +166,7 @@ class MrpProduction(models.Model):
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') for wo_state in
production.workorder_ids.mapped('state')): production.workorder_ids.mapped('state')):
production.state = 'pending_cam' production.state = 'pending_cam'
if production.is_rework is True: if production.is_rework is True:
@@ -218,11 +184,6 @@ class MrpProduction(models.Model):
for wo in for wo in
production.workorder_ids): production.workorder_ids):
production.state = 'rework' production.state = 'rework'
if any(wo.test_results == '报废' and wo.state == 'done' for wo in production.workorder_ids):
production.state = 'scrap'
if any(dr.test_results == '报废' and dr.handle_result == '已处理' for dr in
production.detection_result_ids):
production.state = 'cancel'
# 如果制造订单的功能刀具为【无效刀】则制造订单状态改为返工 # 如果制造订单的功能刀具为【无效刀】则制造订单状态改为返工
if production.tool_state == '2': if production.tool_state == '2':
production.state = 'rework' production.state = 'rework'
@@ -476,45 +437,44 @@ class MrpProduction(models.Model):
# self.env['mrp.workorder'].json_workorder_str(k, production, route)) # self.env['mrp.workorder'].json_workorder_str(k, production, route))
# 表面工艺工序 # 表面工艺工序
# 获取表面工艺id # 获取表面工艺id
# 工序id if production.product_id.model_process_parameters_ids:
surface_technics_arr = [] logging.info('model_process_parameters_ids:%s' % production.product_id.model_process_parameters_ids)
route_workcenter_arr = [] surface_technics_arr = []
for item in production.product_id.product_model_type_id.surface_technics_routing_tmpl_ids: # 工序id
if item.route_workcenter_id.surface_technics_id.id: route_workcenter_arr = []
for process_param in production.product_id.model_process_parameters_ids: for item in production.product_id.product_model_type_id.surface_technics_routing_tmpl_ids:
logging.info('process_param:%s%s' % (process_param.id, process_param.name)) surface_technics_arr.append(item.route_workcenter_id.surface_technics_id.id)
if item.route_workcenter_id.surface_technics_id == process_param.process_id: route_workcenter_arr.append(item.route_workcenter_id.id)
logging.info( if surface_technics_arr:
'surface_technics_id:%s%s' % (item.route_workcenter_id.surface_technics_id.id, production_process_category = self.env['sf.production.process.category'].search(
item.route_workcenter_id.surface_technics_id.name)) [('production_process_ids.id', 'in', surface_technics_arr)],
surface_technics_arr.append(item.route_workcenter_id.surface_technics_id.id) order='sequence asc'
route_workcenter_arr.append(item.route_workcenter_id.id) )
if surface_technics_arr: # 用filter刷选表面工艺id'是否存在工艺类别对象里
production_process = self.env['sf.production.process'].search( if production_process_category:
[('id', 'in', surface_technics_arr)], for p in production_process_category:
order='sequence asc' logging.info('production_process_category:%s' % p.name)
) production_process = p.production_process_ids.filtered(
for p in production_process: lambda pp: pp.id in surface_technics_arr)
logging.info('production_process:%s' % p.name) if production_process:
# if production_process: process_parameter = production.product_id.model_process_parameters_ids.filtered(
process_parameter = production.product_id.model_process_parameters_ids.filtered( lambda pm: pm.process_id.id == production_process.id)
lambda pm: pm.process_id.id == p.id) if process_parameter:
if process_parameter: # 产品为表面工艺服务的供应商
# 产品为表面工艺服务的供应商 product_production_process = self.env['product.template'].search(
product_production_process = self.env['product.template'].search( [('server_product_process_parameters_id', '=', process_parameter.id)])
[('server_product_process_parameters_id', '=', process_parameter.id)]) if product_production_process:
if product_production_process: route_production_process = self.env[
route_production_process = self.env[ 'mrp.routing.workcenter'].search(
'mrp.routing.workcenter'].search( [('surface_technics_id', '=', production_process.id),
[('surface_technics_id', '=', p.id), ('id', 'in', route_workcenter_arr)])
('id', 'in', route_workcenter_arr)]) if route_production_process:
if route_production_process: workorders_values.append(
workorders_values.append( self.env[
self.env[ 'mrp.workorder']._json_workorder_surface_process_str(
'mrp.workorder']._json_workorder_surface_process_str( production, route_production_process,
production, route_production_process, process_parameter,
process_parameter, product_production_process.seller_ids[0].partner_id.id))
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)],
@@ -664,7 +624,7 @@ class MrpProduction(models.Model):
# 表面工艺工序 # 表面工艺工序
# 模型类型的表面工艺工序模版 # 模型类型的表面工艺工序模版
surface_tmpl_ids = model_type_id.surface_technics_routing_tmpl_ids surface_tmpl_ids = model_type_id.surface_technics_routing_tmpl_ids
# 产品选择的表面工艺参数 # 产品选择的表面工艺
model_process_parameters_ids = rec.product_id.model_process_parameters_ids model_process_parameters_ids = rec.product_id.model_process_parameters_ids
process_dict = {} process_dict = {}
if model_process_parameters_ids: if model_process_parameters_ids:
@@ -673,7 +633,7 @@ class MrpProduction(models.Model):
for surface_tmpl_id in surface_tmpl_ids: for surface_tmpl_id in surface_tmpl_ids:
if process_id == surface_tmpl_id.route_workcenter_id.surface_technics_id: if process_id == surface_tmpl_id.route_workcenter_id.surface_technics_id:
surface_tmpl_name = surface_tmpl_id.route_workcenter_id.name surface_tmpl_name = surface_tmpl_id.route_workcenter_id.name
process_dict.update({int(process_id.sequence): '%s-%s' % ( process_dict.update({int(process_id.category_id.code): '%s-%s' % (
surface_tmpl_name, process_parameters_id.name)}) surface_tmpl_name, process_parameters_id.name)})
process_list = sorted(process_dict.keys()) process_list = sorted(process_dict.keys())
for process_num in process_list: for process_num in process_list:
@@ -690,16 +650,14 @@ class MrpProduction(models.Model):
raise ValidationError('该产品【加工面板】为空!') raise ValidationError('该产品【加工面板】为空!')
else: else:
raise ValidationError('该产品没有选择【模版类型】!') raise ValidationError('该产品没有选择【模版类型】!')
logging.info('sequence_list: %s' % sequence_list)
for work in rec.workorder_ids: for work in rec.workorder_ids:
work_name = work.name if sequence_list.get(work.name):
logging.info(work_name) work.sequence = sequence_list[work.name]
if sequence_list.get(work_name):
work.sequence = sequence_list[work_name]
elif sequence_list.get(work.processing_panel): elif sequence_list.get(work.processing_panel):
processing_panel = sequence_list.get(work.processing_panel) processing_panel = sequence_list.get(work.processing_panel)
if processing_panel.get(work_name): if processing_panel.get(work.name):
work.sequence = processing_panel[work_name] work.sequence = processing_panel[work.name]
else: else:
raise ValidationError('工序【%s】在产品选择的模版类型中不存在!' % work.name) raise ValidationError('工序【%s】在产品选择的模版类型中不存在!' % work.name)
else: else:
@@ -725,9 +683,8 @@ class MrpProduction(models.Model):
sequence_max += 1 sequence_max += 1
panel_sequence_list.update({tmpl_id.route_workcenter_id.name: sequence_max}) panel_sequence_list.update({tmpl_id.route_workcenter_id.name: sequence_max})
for work_id in work_ids: for work_id in work_ids:
work_name = work_id.name if panel_sequence_list.get(work_id.name):
if panel_sequence_list.get(work_name): work_id.sequence = panel_sequence_list[work_id.name]
work_id.sequence = panel_sequence_list[work_name]
# 创建工单并进行排序 # 创建工单并进行排序
def _create_workorder(self, item): def _create_workorder(self, item):
@@ -735,52 +692,6 @@ class MrpProduction(models.Model):
self._reset_work_order_sequence() self._reset_work_order_sequence()
return True return True
def process_range_time(self):
for production in self:
works = production.workorder_ids
pro_plan = self.env['sf.production.plan'].search([('production_id', '=', production.id)], limit=1)
if not pro_plan:
continue
type_map = {'装夹预调': False, 'CNC加工': False, '解除装夹': False}
# 最后一次加工结束时间
last_time = pro_plan.date_planned_start
# 预置时间
for work in works:
count = type_map.get(work.routing_type)
date_planned_end = None
date_planned_start = None
duration_expected = datetime.timedelta(minutes=work.duration_expected)
reserve_time = datetime.timedelta(minutes=work.reserved_duration)
if not count:
# 第一轮加工
if work.routing_type == '装夹预调':
date_planned_end = last_time - reserve_time
date_planned_start = date_planned_end - duration_expected
elif work.routing_type == 'CNC加工':
date_planned_start = last_time
date_planned_end = last_time + duration_expected
last_time = date_planned_end
else:
date_planned_start = last_time + reserve_time
date_planned_end = date_planned_start + duration_expected
last_time = date_planned_end
type_map.update({work.routing_type: True})
else:
date_planned_start = last_time + reserve_time
date_planned_end = date_planned_start + duration_expected
last_time = date_planned_end
work.leave_id.write({
'date_from': date_planned_start,
'date_to': date_planned_end,
})
# work.write({'date_planned_start': date_planned_start, 'date_planned_finished': date_planned_end})
work.date_planned_start = date_planned_start
work.date_planned_finished = date_planned_end
routing_workcenter = self.env['mrp.routing.workcenter'].sudo().search(
[('name', '=', work.routing_type)])
work.write({'date_planned_start': date_planned_start, 'date_planned_finished': date_planned_end,'duration_expected':routing_workcenter.time_cycle})
# 修改标记已完成方法 # 修改标记已完成方法
def button_mark_done1(self): def button_mark_done1(self):
if not self.workorder_ids.filtered(lambda w: w.routing_type not in ['表面工艺']): if not self.workorder_ids.filtered(lambda w: w.routing_type not in ['表面工艺']):
@@ -877,23 +788,6 @@ class MrpProduction(models.Model):
}) })
return action return action
# 报废
def button_scrap_new(self):
cloud_programming = self._cron_get_programming_state()
return {
'name': _('报废'),
'type': 'ir.actions.act_window',
'view_mode': 'form',
'res_model': 'sf.production.wizard',
'target': 'new',
'context': {
'default_production_id': self.id,
'default_reprogramming_num': cloud_programming['reprogramming_num'],
'default_programming_states': cloud_programming['programming_state'],
'default_is_reprogramming': True if cloud_programming['programming_state'] in ['已下发'] else False
}
}
# 返工 # 返工
def button_rework(self): def button_rework(self):
cloud_programming = None cloud_programming = None
@@ -1011,6 +905,7 @@ class MrpProduction(models.Model):
# production.write( # production.write(
# {'state': 'progress', 'programming_state': '已编程', 'is_rework': False}) # {'state': 'progress', 'programming_state': '已编程', 'is_rework': False})
# logging.info('返工含有已编程未下发的程序更新完成:%s' % production.name) # logging.info('返工含有已编程未下发的程序更新完成:%s' % production.name)
logging.info('更新程序完成:%s' % production.name)
else: else:
raise UserError(result['message']) raise UserError(result['message'])
@@ -1018,114 +913,116 @@ class MrpProduction(models.Model):
logging.info('get_new_program error:%s' % e) logging.info('get_new_program error:%s' % e)
raise UserError("从云平台获取最新程序失败,请联系管理员") raise UserError("从云平台获取最新程序失败,请联系管理员")
def recreateManufacturing(self, item): def recreateManufacturing(self):
""" """
重新生成制造订单 重新生成制造订单
""" """
if self.is_scrap is True: if self.is_scrap is True:
procurement_requests = [] sale_order = self.env['sale.order'].sudo().search([('name', '=', productions.origin)])
sale_order = self.env['sale.order'].sudo().search([('name', '=', self.origin)]) values = self.env['mrp.production'].create_production1_values(self.production_id)
values = self.env['mrp.production'].create_production1_values(self) productions = self.env['mrp.production'].with_user(SUPERUSER_ID).sudo().with_company(
# productions = self.env['mrp.production'].with_user(SUPERUSER_ID).sudo().with_company( self.production_id.company_id).create(
# self.company_id).create( values)
# values) # self.env['stock.move'].sudo().create(productions._get_moves_raw_values())
# 查询出库移动记录 self.env['stock.move'].sudo().create(productions._get_moves_finished_values())
out_picking = self.env['stock.picking'].search( productions._create_workorder()
[('origin', '=', sale_order.name), ('name', 'ilike', 'WH/OUT/')]) productions.filtered(lambda p: (not p.orderpoint_id and p.move_raw_ids) or \
move = out_picking.move_ids.filtered(lambda pd: pd.product_id == self.product_id) (
move_values = {'product_description_variants': '', p.move_dest_ids.procure_method != 'make_to_order' and
'date_planned': fields.Datetime.now(), not p.move_raw_ids and not p.workorder_ids)).action_confirm()
'date_deadline': fields.Datetime.now(), for production_item in productions:
'move_dest_ids': move, process_parameter_workorder = self.env['mrp.workorder'].search(
'group_id': move.group_id, [('surface_technics_parameters_id', '!=', False), ('production_id', '=', production_item.id),
'route_ids': [], ('is_subcontract', '=', True)])
'warehouse_id': self.warehouse_id, if process_parameter_workorder:
'priority': 0, is_pick = False
'orderpoint_id': False, consecutive_workorders = []
'product_packaging_id': False} m = 0
procurement_requests.append(self.env['procurement.group'].Procurement( sorted_workorders = sorted(process_parameter_workorder, key=lambda w: w.id)
move.product_id, 1.0, move.product_uom, for i in range(len(sorted_workorders) - 1):
move.location_id, move.rule_id and move.rule_id.name or "/", if m == 0:
sale_order.name, move.company_id, move_values)) is_pick = False
self.env['procurement.group'].run(procurement_requests, if sorted_workorders[i].supplier_id.id == sorted_workorders[i + 1].supplier_id.id and \
raise_user_error=not self.env.context.get('from_orderpoint')) sorted_workorders[i].is_subcontract == sorted_workorders[i + 1].is_subcontract and \
productions = self.env['mrp.production'].sudo().search( sorted_workorders[i].id == sorted_workorders[i + 1].id - 1:
[('origin', '=', self.origin)], order='id desc', limit=1) if sorted_workorders[i] not in consecutive_workorders:
move = self.env['stock.move'].search([('origin', '=', productions.name)], order='id desc') consecutive_workorders.append(sorted_workorders[i])
for mo in move: consecutive_workorders.append(sorted_workorders[i + 1])
if mo.procure_method == 'make_to_order' and mo.name != productions.name: m += 1
if mo.name == '/': continue
domain = [('barcode', '=', 'WH-PC'), ('sequence_code', '=', 'PC')] else:
elif mo.name == '': if m == len(consecutive_workorders) - 1 and m != 0:
domain = [('barcode', '=', 'WH-INTERNAL'), ('sequence_code', '=', 'INT')] self.env['stock.picking'].create_outcontract_picking(consecutive_workorders,
picking_type = self.env['stock.picking.type'].search(domain) production_item)
mo.write({'picking_type_id': picking_type.id}) if sorted_workorders[i] in consecutive_workorders:
mo._assign_picking() is_pick = True
else: consecutive_workorders = []
if mo.reference != productions.name: m = 0
mo.reference = productions.name # 当前面的连续工序生成对应的外协出入库单再生成当前工序的外协出入库单
if mo.production_id: if is_pick is False:
if mo.production_id != productions: self.env['stock.picking'].create_outcontract_picking(sorted_workorders[i],
mo.production_id = False production_item)
mo_move = self.env['stock.move'].search( if m == len(consecutive_workorders) - 1 and m != 0:
[('origin', '=', sale_order.name), ('reference', 'ilike', 'WH/MO/')]) self.env['stock.picking'].create_outcontract_picking(consecutive_workorders,
if mo_move: production_item)
sfp_move = self.env['stock.move'].search( if sorted_workorders[i] in consecutive_workorders:
[('origin', '=', sale_order.name), ('reference', 'ilike', 'WH/SFP/')], limit=1) is_pick = True
mo_move.write({'reference': sfp_move.reference, 'partner_id': sfp_move.partner_id.id, consecutive_workorders = []
'picking_id': sfp_move.picking_id.id, 'picking_type_id': sfp_move.picking_type_id.id, m = 0
'production_id': False}) if m == len(consecutive_workorders) - 1 and m != 0:
productions.write({'programming_no': self.programming_no, 'is_remanufacture': True}) self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, production_item)
# productions.procurement_group_id.mrp_production_ids.move_dest_ids.write( if is_pick is False and m == 0:
# {'group_id': self.env['procurement.group'].search([('name', '=', sale_order.name)])}) if len(sorted_workorders) == 1:
stock_picking = None self.env['stock.picking'].create_outcontract_picking(sorted_workorders, production_item)
pc_picking = self.env['stock.picking'].search( else:
[('origin', '=', productions.name), ('name', 'ilike', 'WH/PC/')]) self.env['stock.picking'].create_outcontract_picking(sorted_workorders[i], production_item)
stock_picking = pc_picking
int_picking = self.env['stock.picking'].search( for production in productions:
[('origin', '=', productions.name), ('name', 'ilike', 'WH/INT/')]) origin_production = production.move_dest_ids and production.move_dest_ids[
stock_picking |= int_picking 0].raw_material_production_id or False
for pick in stock_picking: orderpoint = production.orderpoint_id
if pick.move_ids: if orderpoint and orderpoint.create_uid.id == SUPERUSER_ID and orderpoint.trigger == 'manual':
product_type_id = pick.move_ids[0].product_id.categ_id production.message_post(
if product_type_id.name == '坯料': body=_('This production order has been created from Replenishment Report.'),
location_id = self.env['stock.location'].search([('name', '=', '坯料存货区')]) message_type='comment',
if not location_id: subtype_xmlid='mail.mt_note')
logging.info(f'没有搜索到【坯料存货区】: {location_id}') elif orderpoint:
break production.message_post_with_view(
if pick.picking_type_id.name == '内部调拨': 'mail.message_origin_link',
if pick.location_dest_id.product_type != product_type_id: values={'self': production, 'origin': orderpoint},
pick.location_dest_id = location_id.id subtype_id=self.env.ref('mail.mt_note').id)
elif pick.picking_type_id.name == '生产发料': elif origin_production:
if pick.location_id.product_type != product_type_id: production.message_post_with_view(
pick.location_id = location_id.id 'mail.message_origin_link',
scarp_process_parameter_workorder = self.env['mrp.workorder'].search( values={'self': production, 'origin': origin_production},
[('surface_technics_parameters_id', '!=', False), ('production_id', '=', self.id), subtype_id=self.env.ref('mail.mt_note').id)
('is_subcontract', '=', True)])
if scarp_process_parameter_workorder: '''
production_programming = self.env['mrp.production'].search( 创建生产计划
[('programming_no', '=', self.programming_no), ('id', '!=', productions.id)], order='name asc') '''
production_list = [production.name for production in production_programming] # 工单耗时
purchase_orders = self.env['purchase.order'].search([('origin', 'ilike', ','.join(production_list))]) workorder_duration = 0
for purchase_item in purchase_orders.order_line: for workorder in productions.workorder_ids:
for process_item in scarp_process_parameter_workorder: workorder_duration += workorder.duration_expected
if purchase_item.product_id.categ_type == '表面工艺':
if purchase_item.product_id.server_product_process_parameters_id == process_item.surface_technics_parameters_id: if sale_order:
print(purchase_orders.origin.find(productions.name)) sale_order.mrp_production_ids |= productions
if purchase_orders.origin.find(productions.name) == -1: # sale_order.write({'schedule_status': 'to schedule'})
purchase_orders.origin += ',' + productions.name self.env['sf.production.plan'].sudo().with_company(self.production_id.company_id).create({
if item['is_reprogramming'] is False: 'name': productions.name,
productions._create_workorder(item) 'order_deadline': sale_order.deadline_of_delivery,
productions.programming_state = '已编程' 'production_id': productions.id,
else: 'date_planned_start': productions.date_planned_start,
productions.programming_state = '编程中' 'origin': productions.origin,
return productions 'product_qty': productions.product_qty,
'product_id': productions.product_id.id,
'state': 'draft',
})
# 在之前的销售单上重新生成制造订单 # 在之前的销售单上重新生成制造订单
def create_production1_values(self, production): def create_production1_values(self, production, sale_order):
production_values_str = {'origin': production.origin, production_values_str = {'origin': production.origin,
'product_id': production.product_id.id, 'product_id': production.product_id.id,
'programming_state': '已编程',
'product_description_variants': production.product_description_variants, 'product_description_variants': production.product_description_variants,
'product_qty': production.product_qty, 'product_qty': production.product_qty,
'product_uom_id': production.product_uom_id.id, 'product_uom_id': production.product_uom_id.id,
@@ -1135,8 +1032,7 @@ class MrpProduction(models.Model):
'date_deadline': production.date_deadline, 'date_deadline': production.date_deadline,
'date_planned_start': production.date_planned_start, 'date_planned_start': production.date_planned_start,
'date_planned_finished': production.date_planned_finished, 'date_planned_finished': production.date_planned_finished,
# 'procurement_group_id': self.env["procurement.group"].create( 'procurement_group_id': sale_order.id,
# {'name': production.name}).id,
'propagate_cancel': production.propagate_cancel, 'propagate_cancel': production.propagate_cancel,
'orderpoint_id': production.orderpoint_id.id, 'orderpoint_id': production.orderpoint_id.id,
'picking_type_id': production.picking_type_id.id, 'picking_type_id': production.picking_type_id.id,

View File

@@ -21,7 +21,7 @@ class ResMrpRoutingWorkcenter(models.Model):
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)
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])],

View File

@@ -41,7 +41,6 @@ class ResWorkcenter(models.Model):
oee_target = fields.Float( oee_target = fields.Float(
string='OEE Target', help="Overall Effective Efficiency Target in percentage", default=90, tracking=True) string='OEE Target', help="Overall Effective Efficiency Target in percentage", default=90, tracking=True)
oee = fields.Float(compute='_compute_oee', help='Overall Equipment Effectiveness, based on the last month', store=True)
time_start = fields.Float('Setup Time', tracking=True) time_start = fields.Float('Setup Time', tracking=True)
time_stop = fields.Float('Cleanup Time', tracking=True) time_stop = fields.Float('Cleanup Time', tracking=True)
@@ -125,8 +124,6 @@ class ResWorkcenter(models.Model):
res[wc_id] = [(datetime.fromtimestamp(s), datetime.fromtimestamp(e)) for s, e, _ in final_intervals_wc] res[wc_id] = [(datetime.fromtimestamp(s), datetime.fromtimestamp(e)) for s, e, _ in final_intervals_wc]
return res return res
# AGV是否可配送
is_agv_scheduling = fields.Boolean(string="AGV所属区域", tracking=True)
class ResWorkcenterProductivity(models.Model): class ResWorkcenterProductivity(models.Model):
_inherit = 'mrp.workcenter.productivity' _inherit = 'mrp.workcenter.productivity'

View File

@@ -58,15 +58,9 @@ class ResMrpWorkOrder(models.Model):
('cancel', '取消')], string='Status', ('cancel', '取消')], string='Status',
compute='_compute_state', store=True, compute='_compute_state', store=True,
default='pending', copy=False, readonly=True, recursive=True, index=True, tracking=True) default='pending', copy=False, readonly=True, recursive=True, index=True, tracking=True)
# state = fields.Selection(selection_add=[('to be detected', "待检测"), ('rework', '返工')], tracking=True) # state = fields.Selection(selection_add=[('to be detected', "待检测"), ('rework', '返工')], tracking=True)
@api.depends('production_id.manual_quotation') manual_quotation = fields.Boolean('人工编程', default=False, readonly=True)
def _compute_manual_quotation(self):
for item in self:
item.manual_quotation = item.production_id.manual_quotation
manual_quotation = fields.Boolean('人工编程', default=False, compute=_compute_manual_quotation, store=True)
def _compute_working_users(self): def _compute_working_users(self):
super()._compute_working_users() super()._compute_working_users()
@@ -137,32 +131,13 @@ class ResMrpWorkOrder(models.Model):
is_subcontract = fields.Boolean(string='是否外协') is_subcontract = fields.Boolean(string='是否外协')
surface_technics_parameters_id = fields.Many2one('sf.production.process.parameter', string="表面工艺可选参数") surface_technics_parameters_id = fields.Many2one('sf.production.process.parameter', string="表面工艺可选参数")
picking_ids = fields.Many2many('stock.picking', string='外协出入库单') picking_ids = fields.Many2many('stock.picking', string='外协出入库单')
# purchase_id = fields.Many2one('purchase.order', string='外协采购单')
surface_technics_picking_count = fields.Integer("外协出入库", compute='_compute_surface_technics_picking_ids') surface_technics_picking_count = fields.Integer("外协出入库", compute='_compute_surface_technics_picking_ids')
surface_technics_purchase_count = fields.Integer("外协采购", compute='_compute_surface_technics_purchase_ids')
# 是否绑定托盘
is_trayed = fields.Boolean(string='是否绑定托盘', default=False)
@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 order in self:
if workorder.routing_type == '表面工艺': picking_ids = self.env['stock.picking'].search([('id', 'in', order.picking_ids.ids)])
domain = [('origin', '=', workorder.production_id.name)] order.surface_technics_picking_count = len(picking_ids)
previous_workorder = self.env['mrp.workorder'].search(
[('sequence', '=', workorder.sequence - 1), ('routing_type', '=', '表面工艺'),
('production_id', '=', workorder.production_id.id)])
if previous_workorder:
process_product = self.env['product.template']._get_process_parameters_product(
previous_workorder.surface_technics_parameters_id)
domain += [('partner_id', '=', process_product.partner_id.id)]
else:
domain += [('surface_technics_parameters_id', '=', workorder.surface_technics_parameters_id.id)]
picking_ids = self.env['stock.picking'].search(domain, order='id asc')
workorder.surface_technics_picking_count = len(picking_ids)
workorder.picking_ids = picking_ids.ids
else:
workorder.surface_technics_picking_count = 0
def action_view_surface_technics_picking(self): def action_view_surface_technics_picking(self):
self.ensure_one() self.ensure_one()
@@ -178,38 +153,6 @@ class ResMrpWorkOrder(models.Model):
action['context'] = dict(self._context, default_origin=self.name) action['context'] = dict(self._context, default_origin=self.name)
return action return action
@api.depends('state', 'production_id.name')
def _compute_surface_technics_purchase_ids(self):
for order in self:
if order.routing_type == '表面工艺':
production_programming = self.env['mrp.production'].search(
[('programming_no', '=', order.production_id.programming_no)], order='name asc')
production_no_remanufacture = production_programming.filtered(lambda a: a.is_remanufacture is False)
production_list = [production.name for production in production_programming]
purchase = self.env['purchase.order'].search([('origin', '=', ','.join(production_list))])
for line in purchase.order_line:
if line.product_id.server_product_process_parameters_id == order.surface_technics_parameters_id and line.product_qty == len(
production_no_remanufacture):
order.surface_technics_purchase_count = len(purchase)
else:
order.surface_technics_purchase_count = 0
def action_view_surface_technics_purchase(self):
self.ensure_one()
production_programming = self.env['mrp.production'].search(
[('programming_no', '=', self.production_id.programming_no)], order='name asc')
production_list = [production.name for production in production_programming]
purchase_orders = self.env['purchase.order'].search([('origin', '=', ','.join(production_list))])
result = {
"type": "ir.actions.act_window",
"res_model": "purchase.order",
"res_id": purchase_orders.id,
# "domain": [['id', 'in', self.purchase_id]],
"name": _("Purchase Orders"),
'view_mode': 'form',
}
return result
supplier_id = fields.Many2one('res.partner', string='外协供应商') supplier_id = fields.Many2one('res.partner', string='外协供应商')
equipment_id = fields.Many2one('maintenance.equipment', string='加工设备', tracking=True) equipment_id = fields.Many2one('maintenance.equipment', string='加工设备', tracking=True)
# 保存名称 # 保存名称
@@ -238,7 +181,6 @@ class ResMrpWorkOrder(models.Model):
tool_state = fields.Selection([('0', '正常'), ('1', '缺刀'), ('2', '无效刀')], string='功能刀具状态', default='0', tool_state = fields.Selection([('0', '正常'), ('1', '缺刀'), ('2', '无效刀')], string='功能刀具状态', default='0',
store=True, compute='_compute_tool_state') store=True, compute='_compute_tool_state')
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)
reserved_duration = fields.Float('预留时长', default=30, tracking=True)
@api.depends('cnc_ids.tool_state') @api.depends('cnc_ids.tool_state')
def _compute_tool_state_remark(self): def _compute_tool_state_remark(self):
@@ -376,10 +318,10 @@ class ResMrpWorkOrder(models.Model):
vals['leave_id'] = leave.id vals['leave_id'] = leave.id
self.write(vals) self.write(vals)
# @api.onchange('rfid_code') @api.onchange('rfid_code')
# def _onchange(self): def _onchange(self):
# if self.rfid_code and self.state == 'progress': if self.rfid_code and self.state == 'progress':
# self.workpiece_delivery_ids[0].write({'rfid_code': self.rfid_code}) self.workpiece_delivery_ids[0].write({'rfid_code': self.rfid_code})
def get_plan_workorder(self, production_line): def get_plan_workorder(self, production_line):
tomorrow = (date.today() + timedelta(days=+1)).strftime("%Y-%m-%d") tomorrow = (date.today() + timedelta(days=+1)).strftime("%Y-%m-%d")
@@ -648,36 +590,29 @@ class ResMrpWorkOrder(models.Model):
# 拼接工单对象属性值 # 拼接工单对象属性值
def json_workorder_str(self, k, production, route, item): def json_workorder_str(self, k, production, route, item):
# 计算预计时长duration_expected # 计算预计时长duration_expected
routing_types = ['切割', '装夹预调', 'CNC加工', '解除装夹'] if route.routing_type == '切割':
if route.routing_type in routing_types: duration_expected = self.env['mrp.routing.workcenter'].sudo().search(
routing_workcenter = self.env['mrp.routing.workcenter'].sudo().search( [('name', '=', '切割')]).time_cycle
[('name', '=', route.routing_type)]) # elif route.routing_type == '获取CNC加工程序':
duration_expected = routing_workcenter.time_cycle
reserved_duration = routing_workcenter.reserved_duration
# if route.routing_type == '切割':
# duration_expected = self.env['mrp.routing.workcenter'].sudo().search( # duration_expected = self.env['mrp.routing.workcenter'].sudo().search(
# [('name', '=', '切割')]).time_cycle # [('name', '=', '获取CNC加工程序')]).time_cycle
# # elif route.routing_type == '获取CNC加工程序': elif route.routing_type == '装夹预调':
# # duration_expected = self.env['mrp.routing.workcenter'].sudo().search( duration_expected = self.env['mrp.routing.workcenter'].sudo().search(
# # [('name', '=', '获取CNC加工程序')]).time_cycle [('name', '=', '装夹预调')]).time_cycle
# elif route.routing_type == '装夹预调': # elif route.routing_type == '前置三元定位检测':
# duration_expected = self.env['mrp.routing.workcenter'].sudo().search( # duration_expected = self.env['mrp.routing.workcenter'].sudo().search(
# [('name', '=', '装夹预调')]).time_cycle # [('name', '=', '前置三元定位检测')]).time_cycle
# # elif route.routing_type == '前置三元定位检测': elif route.routing_type == 'CNC加工':
# # duration_expected = self.env['mrp.routing.workcenter'].sudo().search( duration_expected = self.env['mrp.routing.workcenter'].sudo().search(
# # [('name', '=', '前置三元定位检测')]).time_cycle [('name', '=', 'CNC加工')]).time_cycle
# elif route.routing_type == 'CNC加工': # elif route.routing_type == '后置三元质量检测':
# duration_expected = self.env['mrp.routing.workcenter'].sudo().search( # duration_expected = self.env['mrp.routing.workcenter'].sudo().search(
# [('name', '=', 'CNC加工')]).time_cycle # [('name', '=', '后置三元质量检测')]).time_cycle
# # elif route.routing_type == '后置三元质量检测': elif route.routing_type == '解除装夹':
# # duration_expected = self.env['mrp.routing.workcenter'].sudo().search( duration_expected = self.env['mrp.routing.workcenter'].sudo().search(
# # [('name', '=', '后置三元质量检测')]).time_cycle [('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
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,
@@ -699,36 +634,25 @@ class ResMrpWorkOrder(models.Model):
k, item), k, item),
'cmm_ids': False if route.routing_type != 'CNC加工' else self.env['sf.cmm.program']._json_cmm_program(k, 'cmm_ids': False if route.routing_type != 'CNC加工' else self.env['sf.cmm.program']._json_cmm_program(k,
item), item),
# 'workpiece_delivery_ids': False if not route.routing_type == '装夹预调' else self._json_workpiece_delivery_list( 'workpiece_delivery_ids': False if not route.routing_type == '装夹预调' else self._json_workpiece_delivery_list(
# production) production)
'reserved_duration': reserved_duration,
}] }]
return workorders_values_str return workorders_values_str
def _json_workpiece_delivery_list(self): def _json_workpiece_delivery_list(self, production):
# 修改在装夹工单完成后,生成上产线的工件配送单 up_route = self.env['sf.agv.task.route'].search([('route_type', '=', '上产线')], limit=1, order='id asc')
down_route = self.env['sf.agv.task.route'].search([('route_type', '=', '下产线')], limit=1, order='id asc')
# up_route = self.env['sf.agv.task.route'].search([('route_type', '=', '上产线')], limit=1, order='id asc')
# down_route = self.env['sf.agv.task.route'].search([('route_type', '=', '下产线')], limit=1, order='id asc')
return [ return [
[0, '', [0, '',
{ {'production_id': production.id, 'production_line_id': production.production_line_id.id, 'type': '上产线',
'production_id': self.production_id.id, 'route_id': up_route.id,
'production_line_id': self.production_id.production_line_id.id, 'feeder_station_start_id': up_route.start_site_id.id,
'type': '上产线', 'feeder_station_destination_id': up_route.end_site_id.id}],
'is_cnc_program_down': True, [0, '',
'rfid_code': self.rfid_code {'production_id': production.id, 'production_line_id': production.production_line_id.id, 'type': '下产线',
# 'route_id': up_route.id, 'route_id': down_route.id,
# 'feeder_station_start_id': agv_start_site_id, 'feeder_station_start_id': down_route.start_site_id.id,
# 'feeder_station_destination_id': up_route.end_site_id.id 'feeder_station_destination_id': down_route.end_site_id.id}]]
}
],
# [0, '',
# {'production_id': production.id, 'production_line_id': production.production_line_id.id, 'type': '下产线',
# 'route_id': down_route.id,
# 'feeder_station_start_id': down_route.start_site_id.id,
# 'feeder_station_destination_id': down_route.end_site_id.id}]
]
# 拼接工单对象属性值(表面工艺) # 拼接工单对象属性值(表面工艺)
def _json_workorder_surface_process_str(self, production, route, process_parameter, supplier_id): def _json_workorder_surface_process_str(self, production, route, process_parameter, supplier_id):
@@ -944,7 +868,7 @@ class ResMrpWorkOrder(models.Model):
workorder.state = 'waiting' workorder.state = 'waiting'
elif workorder.routing_type == '解除装夹' and workorder.state not in ['done', 'rework', 'cancel']: elif workorder.routing_type == '解除装夹' and workorder.state not in ['done', 'rework', 'cancel']:
if cnc_workorder: if cnc_workorder:
if not cnc_workorder_pending or unclamp_workorder.test_results == '报废': if not cnc_workorder_pending:
workorder.state = 'waiting' workorder.state = 'waiting'
# else: # else:
# if workorder.production_id.is_rework is True: # if workorder.production_id.is_rework is True:
@@ -961,26 +885,10 @@ class ResMrpWorkOrder(models.Model):
# workorder.state = 'ready' # workorder.state = 'ready'
if workorder.routing_type == '表面工艺' and workorder.state not in ['done', 'progress']: if workorder.routing_type == '表面工艺' and workorder.state not in ['done', 'progress']:
if unclamp_workorder: if unclamp_workorder:
if workorder.is_subcontract is False: workorder.state = 'ready'
workorder.state = 'ready' # else:
else: # if workorder.state not in ['cancel', 'rework']:
production_programming = self.env['mrp.production'].search( # workorder.state = 'rework'
[('programming_no', '=', self.production_id.programming_no)], order='name asc')
production_no_remanufacture = production_programming.filtered(
lambda a: a.is_remanufacture is False)
production_list = [production.name for production in production_programming]
purchase_orders = self.env['purchase.order'].search(
[('origin', 'ilike', ','.join(production_list))])
for line in purchase_orders.order_line:
if line.product_id.server_product_process_parameters_id == workorder.surface_technics_parameters_id and line.product_qty == len(
production_no_remanufacture):
if purchase_orders.state == 'purchase':
workorder.state = 'ready'
else:
workorder.state = 'waiting'
elif workorder.production_id.state == 'scrap':
if workorder.routing_type == '解除装夹' and unclamp_workorder.test_results == '报废':
workorder.state = 'waiting'
if workorder.routing_type == '装夹预调' and workorder.state in ['waiting', 'ready', 'pending']: 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
@@ -1099,7 +1007,7 @@ class ResMrpWorkOrder(models.Model):
('location_dest_id', '=', self.env['stock.location'].search( ('location_dest_id', '=', self.env['stock.location'].search(
[('barcode', 'ilike', 'VL-SPOC')]).id), [('barcode', 'ilike', 'VL-SPOC')]).id),
('origin', '=', self.production_id.name)]) ('origin', '=', self.production_id.name)])
if move_out.state != 'done': if move_out:
move_out.write({'state': 'assigned'}) move_out.write({'state': 'assigned'})
self.env['stock.move.line'].create(move_out.get_move_line(self.production_id, self)) self.env['stock.move.line'].create(move_out.get_move_line(self.production_id, self))
@@ -1166,31 +1074,23 @@ class ResMrpWorkOrder(models.Model):
def button_finish(self): def button_finish(self):
for record in self: for record in self:
if record.routing_type == '装夹预调': if record.routing_type == '装夹预调':
if not record.material_center_point and record.X_deviation_angle > 0:
raise UserError("请对前置三元检测定位参数进行计算定位")
if not record.rfid_code and record.is_rework is False: if not record.rfid_code and record.is_rework is False:
raise UserError("请扫RFID码进行绑定") raise UserError("请扫RFID码进行绑定")
if record.is_rework is False:
if not record.material_center_point and record.X_deviation_angle > 0:
raise UserError("坯料中心点为空或X偏差角度小于等于0")
record.process_state = '待加工' record.process_state = '待加工'
# record.write({'process_state': '待加工'}) # record.write({'process_state': '待加工'})
record.production_id.process_state = '待加工' record.production_id.process_state = '待加工'
# 生成工件配送单
record.workpiece_delivery_ids = record._json_workpiece_delivery_list()
if record.routing_type == 'CNC加工': if record.routing_type == 'CNC加工':
record.process_state = '待解除装夹' record.process_state = '待解除装夹'
# record.write({'process_state': '待加工'}) # record.write({'process_state': '待加工'})
record.production_id.process_state = '待解除装夹' record.production_id.process_state = '待解除装夹'
self.env['sf.production.plan'].sudo().search([('name', '=', record.production_id.name)]).write({
'state': 'finished',
'actual_end_time': datetime.now()
})
record.production_id.write({'detection_result_ids': [(0, 0, { record.production_id.write({'detection_result_ids': [(0, 0, {
'rework_reason': record.reason, 'rework_reason': record.reason,
'detailed_reason': record.detailed_reason, 'detailed_reason': record.detailed_reason,
'processing_panel': record.processing_panel, 'processing_panel': record.processing_panel,
'routing_type': record.routing_type, 'routing_type': record.routing_type,
'handle_result': '待处理' if record.test_results in ['返工', 'handle_result': '待处理' if record.test_results == '返工' or record.is_rework is True else '',
'报废'] or record.is_rework is True else '',
'test_results': record.test_results, 'test_results': record.test_results,
'test_report': record.detection_report})], 'test_report': record.detection_report})],
'is_scrap': True if record.test_results == '报废' else False}) 'is_scrap': True if record.test_results == '报废' else False})
@@ -1210,13 +1110,28 @@ class ResMrpWorkOrder(models.Model):
picking_out = record.env['stock.move.line'].search( picking_out = record.env['stock.move.line'].search(
[('picking_id', '=', record.picking_ids[0].id)]) [('picking_id', '=', record.picking_ids[0].id)])
logging.info('picking_out:%s' % picking_out.picking_id.name) logging.info('picking_out:%s' % picking_out.picking_id.name)
# if picking_out: if picking_out:
# order_line_ids = [] order_line_ids = []
# logging.info('surface_technics_parameters_id:%s' % record.surface_technics_parameters_id.name) logging.info('surface_technics_parameters_id:%s' % record.surface_technics_parameters_id.name)
# server_product = self.env['product.template'].search(
# else: [('server_product_process_parameters_id', '=', record.surface_technics_parameters_id.id),
# raise UserError( ('detailed_type', '=', 'service')])
# '请先在产品中配置表面工艺为%s相关的外协服务产品' % item.surface_technics_parameters_id.name) logging.info('server_product:%s' % server_product.name)
if server_product:
order_line_ids.append((0, 0, {
'product_id': server_product.product_variant_id.id,
'product_qty': 1,
'product_uom': server_product.uom_id.id
}))
self.env['purchase.order'].sudo().create({
'partner_id': server_product.seller_ids.partner_id.id,
'origin': record.production_id.name,
'state': 'draft',
'order_line': order_line_ids,
})
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)
@@ -1268,17 +1183,6 @@ class ResMrpWorkOrder(models.Model):
record.production_id.button_mark_done1() record.production_id.button_mark_done1()
# record.production_id.state = 'done' # record.production_id.state = 'done'
# 解绑托盘
def unbind_tray(self):
self.production_id.workorder_ids.write({
'rfid_code': False,
'tray_serial_number': False,
'tray_product_id': False,
'tray_brand_id': False,
'tray_type_id': False,
'tray_model_id': False,
'is_trayed': False})
# 将FTP的检测报告文件下载到临时目录 # 将FTP的检测报告文件下载到临时目录
def download_reportfile_tmp(self, workorder, reportpath): def download_reportfile_tmp(self, workorder, reportpath):
logging.info('reportpath/ftp地址:%s' % reportpath) logging.info('reportpath/ftp地址:%s' % reportpath)
@@ -1318,66 +1222,6 @@ class ResMrpWorkOrder(models.Model):
else: else:
raise UserError("无关联制造订单或关联序列号,无法打印。请检查!") raise UserError("无关联制造订单或关联序列号,无法打印。请检查!")
@api.model
def get_views(self, views, options=None):
res = super().get_views(views, options)
if res['views'].get('list', {}) and self.env.context.get('search_default_workcenter_id'):
workcenter = self.env['mrp.workcenter'].browse(self.env.context.get('search_default_workcenter_id'))
tree_view = res['views']['list']
if workcenter.name == '工件拆卸中心':
arch = etree.fromstring(tree_view['arch'])
# 查找 tree 标签
tree_element = arch.xpath("//tree")[0]
# 查找或创建 header 标签
header_element = tree_element.find('header')
if header_element is None:
header_element = etree.Element('header')
tree_element.insert(0, header_element)
# 创建并添加按钮元素
button_element = etree.Element('button', {
'name': 'button_delivery',
'type': 'object',
'string': '解除装夹',
'class': 'btn-primary',
# 'className': 'btn-primary',
'modifiers': '{"force_show": 1}'
})
header_element.append(button_element)
# 更新 tree_view 的 arch
tree_view['arch'] = etree.tostring(arch, encoding='unicode')
return res
def button_delivery(self):
production_ids = []
workorder_ids = []
delivery_type = '运送空料架'
max_num = 4 # 最大配送数量
if len(self) > max_num:
raise UserError('仅限于拆卸1-4个制造订单请重新选择')
for item in self:
if item.state != 'ready':
raise UserError('请选择状态为【就绪】的工单进行解除装夹')
production_ids.append(item.production_id.id)
workorder_ids.append(item.id)
return {
'name': _('确认'),
'type': 'ir.actions.act_window',
'view_mode': 'form',
'res_model': 'sf.workpiece.delivery.wizard',
'target': 'new',
'context': {
# 'default_delivery_ids': [(6, 0, delivery_ids)],
'default_production_ids': [(6, 0, production_ids)],
'default_delivery_type': delivery_type,
'default_workorder_ids': [(6, 0, workorder_ids)],
'default_workcenter_id': self.env.context.get('default_workcenter_id'),
'default_confirm_button': '确认解除'
}}
class CNCprocessing(models.Model): class CNCprocessing(models.Model):
_name = 'sf.cnc.processing' _name = 'sf.cnc.processing'
@@ -1586,7 +1430,6 @@ class SfWorkOrderBarcodes(models.Model):
raise UserError('该Rfid【%s】绑定的是【%s】, 不是托盘!!!' % (barcode, lot.product_id.name)) raise UserError('该Rfid【%s】绑定的是【%s】, 不是托盘!!!' % (barcode, lot.product_id.name))
self.process_state = '待检测' self.process_state = '待检测'
self.date_start = datetime.now() self.date_start = datetime.now()
self.is_trayed = True
else: else:
raise UserError('没有找到Rfid为【%s】的托盘信息!!!' % barcode) raise UserError('没有找到Rfid为【%s】的托盘信息!!!' % barcode)
# stock_move_line = self.env['stock.move.line'].search([('lot_name', '=', barcode)]) # stock_move_line = self.env['stock.move.line'].search([('lot_name', '=', barcode)])
@@ -1658,24 +1501,20 @@ class WorkPieceDelivery(models.Model):
feeder_station_destination_id = fields.Many2one('sf.agv.site', '目的接驳站') feeder_station_destination_id = fields.Many2one('sf.agv.site', '目的接驳站')
task_delivery_time = fields.Datetime('任务下发时间') task_delivery_time = fields.Datetime('任务下发时间')
task_completion_time = fields.Datetime('任务完成时间') task_completion_time = fields.Datetime('任务完成时间')
type = fields.Selection(
def _get_agv_route_type_selection(self): [('上产线', '上产线'), ('下产线', '下产线'), ('运送空料架', '运送空料架')], string='类型')
return self.env['sf.agv.task.route'].fields_get(['route_type'])['route_type']['selection']
type = fields.Selection(selection=_get_agv_route_type_selection, string='类型')
delivery_duration = fields.Float('配送时长', compute='_compute_delivery_duration') delivery_duration = fields.Float('配送时长', compute='_compute_delivery_duration')
status = fields.Selection( status = fields.Selection(
[('待下发', '待下发'), ('已下发', '待配送'), ('已配送', '已配送'), ('已取消', '已取消')], string='状态', [('待下发', '待下发'), ('待配送', '待配送'), ('已配送', '已配送'), ('已取消', '已取消')], string='状态',
default='待下发', tracking=True) default='待下发',
tracking=True)
is_cnc_program_down = fields.Boolean('程序是否下发', default=False, tracking=True) is_cnc_program_down = fields.Boolean('程序是否下发', default=False, tracking=True)
is_manual_work = fields.Boolean('人工操作', default=False) is_manual_work = fields.Boolean('人工操作', default=False)
active = fields.Boolean(string="有效", default=True) active = fields.Boolean(string="有效", default=True)
agv_scheduling_id = fields.Many2one('sf.agv.scheduling', 'AGV任务调度')
@api.model @api.model
def create(self, vals): def create(self, vals):
if vals.get('route_id') and vals.get('type') is None: if vals['route_id'] and vals.get('type') is None:
vals['type'] = '运送空料架' vals['type'] = '运送空料架'
else: else:
if vals.get('name', '/') == '/' or vals.get('name', '/') is False: if vals.get('name', '/') == '/' or vals.get('name', '/') is False:
@@ -1687,14 +1526,14 @@ class WorkPieceDelivery(models.Model):
obj.feeder_station_start_id.name, obj.feeder_station_destination_id.name) obj.feeder_station_start_id.name, obj.feeder_station_destination_id.name)
return obj return obj
# @api.constrains('route_id') @api.constrains('route_id')
# def _check_route_id(self): def _check_route_id(self):
# if self.type == '运送空料架': if self.type == '运送空料架':
# if self.route_id and self.name is False: if self.route_id and self.name is False:
# route = self.sudo().search( route = self.sudo().search(
# [('route_id', '=', self.route_id.id), ('id', '!=', self.id), ('name', 'ilike', '运送空料架路线')]) [('route_id', '=', self.route_id.id), ('id', '!=', self.id), ('name', 'ilike', '运送空料架路线')])
# if route: if route:
# raise UserError("该任务路线已存在,请重新选择") raise UserError("该任务路线已存在,请重新选择")
# @api.constrains('name') # @api.constrains('name')
# def _check_name(self): # def _check_name(self):
@@ -1723,44 +1562,84 @@ class WorkPieceDelivery(models.Model):
def button_delivery(self): def button_delivery(self):
delivery_ids = [] delivery_ids = []
production_ids = [] production_ids = []
workorder_ids = []
is_cnc_down = 0 is_cnc_down = 0
is_not_production_line = 0 is_not_production_line = 0
is_not_route = 0
same_production_line_id = None same_production_line_id = None
delivery_type = '上产线' same_route_id = None
max_num = 4 # 最大配送数量 down_status = '待下发'
if len(self) > max_num: production_type = None
raise UserError('仅限于配送1-4个制造订单请重新选择') num = 0
for item in self: for item in self:
if item.status != '待下发': num += 1
raise UserError('请选择状态为【待下发】的制造订单进行配送') if production_type is None:
if same_production_line_id is None: production_type = item.type
same_production_line_id = item.production_line_id.id if item.type == "运送空料架":
if item.production_line_id.id != same_production_line_id: if num >= 2:
is_not_production_line += 1 raise UserError('仅选择一条路线进行配送,请重新选择')
if item.is_cnc_program_down is False: else:
is_cnc_down += 1 delivery_ids.append(item.id)
if is_cnc_down == 0 and is_not_production_line == 0: else:
delivery_ids.append(item.id) if num > 4:
production_ids.append(item.production_id.id) raise UserError('仅限于配送1-4个制造订单请重新选择')
workorder_ids.append(item.workorder_id.id) if item.status in ['待配送', '已配送']:
raise UserError('请选择状态为【待下发】的制造订单进行配送')
if item.route_id:
if same_route_id is None:
same_route_id = item.route_id.id
if item.route_id.id != same_route_id:
is_not_route += 1
# else:
# raise UserError('请选择【任务路线】再进行配送')
# if item.production_id.production_line_state == '已下产线' and item.state == '待下发' and item.type == '下产线':
# raise UserError('该制造订单已下产线,无需配送')
if production_type != item.type:
raise UserError('请选择类型为%s的制造订单进行配送' % production_type)
if down_status != item.status:
up_workpiece = self.search([('type', '=', '上产线'), ('production_id', '=', item.production_id),
('status', '=', '待下发')])
if up_workpiece:
raise UserError('您所选择的制造订单暂未上产线,请在上产线后再进行配送')
else:
raise UserError('请选择状态为【待下发】的制造订单进行配送')
if same_production_line_id is None:
same_production_line_id = item.production_line_id.id
if item.production_line_id.id != same_production_line_id:
is_not_production_line += 1
if item.is_cnc_program_down is False:
is_cnc_down += 1
if is_cnc_down == 0 and is_not_production_line == 0 and is_not_route == 0:
delivery_ids.append(item.id)
production_ids.append(item.production_id.id)
if is_cnc_down >= 1: if is_cnc_down >= 1:
raise UserError('您所选择制造订单的【CNC程序】暂未下发请在程序下发后再进行配送') raise UserError('您所选择制造订单的【CNC程序】暂未下发请在程序下发后再进行配送')
if is_not_production_line >= 1: if is_not_production_line >= 1:
raise UserError('您所选择制造订单的【目的生产线】不一致,请重新确认') raise UserError('您所选择制造订单的【目的生产线】不一致,请重新确认')
return { if is_not_route >= 1:
'name': _('确认'), raise UserError('您所选择制造订单的【任务路线】不一致,请重新确认')
'type': 'ir.actions.act_window', is_free = self._check_avgsite_state()
'view_mode': 'form', if is_free is True:
'res_model': 'sf.workpiece.delivery.wizard', if delivery_ids:
'target': 'new', return {
'context': { 'name': _('确认'),
'default_delivery_ids': [(6, 0, delivery_ids)], 'type': 'ir.actions.act_window',
'default_production_ids': [(6, 0, production_ids)], 'view_mode': 'form',
'default_delivery_type': delivery_type, 'res_model': 'sf.workpiece.delivery.wizard',
'default_workorder_ids': [(6, 0, workorder_ids)], 'target': 'new',
'default_confirm_button': '确认配送' 'context': {
}} 'default_delivery_ids': [(6, 0, delivery_ids)],
'default_production_ids': [(6, 0, production_ids)],
'default_destination_production_line_id': same_production_line_id,
'default_route_id': same_route_id,
'default_type': production_type,
}}
else:
if production_type == '运送空料架':
raise UserError("您所选择的【任务路线】的【终点接驳站】已占用,请在该接驳站空闲时进行配送")
else:
raise UserError(
"您所选择制造订单的【任务路线】的【终点接驳站】已占用,请在该接驳站空闲时或选择其他路线进行配送")
# 验证agv站点是否可用 # 验证agv站点是否可用
def _check_avgsite_state(self): def _check_avgsite_state(self):

View File

@@ -5,10 +5,8 @@ import base64
import hashlib import hashlib
import os import os
from odoo import models, fields, api, _ from odoo import models, fields, api, _
from odoo.exceptions import ValidationError, UserError from odoo.exceptions import ValidationError
from odoo.modules import get_resource_path from odoo.modules import get_resource_path
from OCC.Extend.DataExchange import read_step_file from OCC.Extend.DataExchange import read_step_file
from OCC.Extend.DataExchange import write_stl_file from OCC.Extend.DataExchange import write_stl_file
@@ -108,15 +106,6 @@ class ResProductMo(models.Model):
name = fields.Char('产品名称', compute='_compute_tool_name', store=True, required=False) name = fields.Char('产品名称', compute='_compute_tool_name', store=True, required=False)
@api.constrains('seller_ids')
def _check_seller_ids(self):
if self.categ_type == '表面工艺':
if self.seller_ids:
if self.seller_ids[0].price == 0.0:
raise UserError("请在该产品【采购】中的【价格】进行输入")
else:
raise UserError("请在【采购】中输入供应商信息")
@api.depends('cutting_tool_model_id', 'specification_id') @api.depends('cutting_tool_model_id', 'specification_id')
def _compute_tool_name(self): def _compute_tool_name(self):
for item in self: for item in self:
@@ -124,10 +113,6 @@ class ResProductMo(models.Model):
name = '%s%s' % (item.cutting_tool_model_id.name, item.specification_id.name) name = '%s%s' % (item.cutting_tool_model_id.name, item.specification_id.name)
item.name = name item.name = name
def _get_process_parameters_product(self, production_process):
return self.env['product.template'].search(
[('server_product_process_parameters_id', '=', production_process.id)]).seller_ids[0]
@api.onchange('cutting_tool_model_id') @api.onchange('cutting_tool_model_id')
def _onchange_cutting_tool_model_id(self): def _onchange_cutting_tool_model_id(self):
for item in self: for item in self:
@@ -655,10 +640,6 @@ class ResProductMo(models.Model):
'part_number': item.get('part_number') or '', 'part_number': item.get('part_number') or '',
'active': True, 'active': True,
} }
tax_id = self.env['account.tax'].sudo().search(
[('type_tax_use', '=', 'sale'), ('amount', '=', item.get('tax')), ('price_include', '=', 'True')])
if tax_id:
vals.update({'taxes_id': [(6, 0, [int(tax_id)])]})
copy_product_id.sudo().write(vals) copy_product_id.sudo().write(vals)
product_id.product_tmpl_id.active = False product_id.product_tmpl_id.active = False
return copy_product_id return copy_product_id
@@ -755,11 +736,7 @@ class ResProductMo(models.Model):
# 产品名称唯一性校验 # 产品名称唯一性校验
for item in templates: for item in templates:
if len(self.search([('name', '=', item.name)])) > 1: if len(self.search([('name', '=', item.name)])) > 1:
raise UserError('产品名称【%s】已存在' % item.name) raise ValidationError('产品名称【%s】已存在' % item.name)
if item.categ_type == '表面工艺':
if len(self.search([('server_product_process_parameters_id', '=',
item.server_product_process_parameters_id.id)])) > 1:
raise UserError('表面工艺参数为【%s】的产品已存在' % item.server_product_process_parameters_id.name)
if "create_product_product" not in self._context: if "create_product_product" not in self._context:
templates._create_variant_ids() templates._create_variant_ids()
@@ -823,7 +800,7 @@ class ResProductFixture(models.Model):
diameter = fields.Float('直径(mm)', digits=(16, 2)) diameter = fields.Float('直径(mm)', digits=(16, 2))
# '零点卡盘' 字段 # '零点卡盘' 字段
weight = fields.Float('重量(kg)', digits=(16, 2)) weight = fields.Float('重量(mm)', digits=(16, 2))
orientation_dish_diameter = fields.Float('定位盘直径(mm)', digits=(16, 2)) orientation_dish_diameter = fields.Float('定位盘直径(mm)', digits=(16, 2))
clamping_diameter = fields.Float('装夹直径(mm)', digits=(16, 2)) clamping_diameter = fields.Float('装夹直径(mm)', digits=(16, 2))
clamping_num = fields.Selection([('1', '1'), ('2', '2'), ('4', '4'), ('6', '6'), ('8', '8')], string='装夹单元数') clamping_num = fields.Selection([('1', '1'), ('2', '2'), ('4', '4'), ('6', '6'), ('8', '8')], string='装夹单元数')
@@ -970,7 +947,6 @@ class SfMaintenanceEquipmentAndProductTemplate(models.Model):
raise ValidationError("机床基坐标获取失败") raise ValidationError("机床基坐标获取失败")
class SfMaintenanceEquipmentTool(models.Model): class SfMaintenanceEquipmentTool(models.Model):
_name = 'maintenance.equipment.tool' _name = 'maintenance.equipment.tool'
_description = '机床刀位' _description = '机床刀位'

View File

@@ -1,22 +0,0 @@
from odoo import models, fields, api
class ResConfigSettings(models.TransientModel):
_inherit = 'res.config.settings'
is_agv_task_dispatch = fields.Boolean('是否下发AGV任务', default=False)
@api.model
def get_values(self):
values = super(ResConfigSettings, self).get_values()
config = self.env['ir.config_parameter'].sudo()
is_agv_task_dispatch = config.get_param('is_agv_task_dispatch')
values.update(
is_agv_task_dispatch=is_agv_task_dispatch,
)
return values
def set_values(self):
super(ResConfigSettings, self).set_values()
config = self.env['ir.config_parameter'].sudo()
config.set_param("is_agv_task_dispatch", self.is_agv_task_dispatch or False)

View File

@@ -68,7 +68,6 @@ class StockRule(models.Model):
@api.model @api.model
def _run_pull(self, procurements): def _run_pull(self, procurements):
logging.info(procurements)
moves_values_by_company = defaultdict(list) moves_values_by_company = defaultdict(list)
mtso_products_by_locations = defaultdict(list) mtso_products_by_locations = defaultdict(list)
@@ -169,6 +168,7 @@ class StockRule(models.Model):
else: else:
forecasted_qties_by_loc[rule.location_src_id][procurement.product_id.id] -= qty_needed forecasted_qties_by_loc[rule.location_src_id][procurement.product_id.id] -= qty_needed
procure_method = 'make_to_stock' procure_method = 'make_to_stock'
move_values = rule._get_stock_move_values(*procurement) move_values = rule._get_stock_move_values(*procurement)
move_values['procure_method'] = procure_method move_values['procure_method'] = procure_method
moves_values_by_company[procurement.company_id.id].append(move_values) moves_values_by_company[procurement.company_id.id].append(move_values)
@@ -176,10 +176,10 @@ class StockRule(models.Model):
for company_id, moves_values in moves_values_by_company.items(): for company_id, moves_values in moves_values_by_company.items():
# create the move as SUPERUSER because the current user may not have the rights to do it (mto product # create the move as SUPERUSER because the current user may not have the rights to do it (mto product
# launched by a sale for example) # launched by a sale for example)
moves = self.env['stock.move'].with_user(SUPERUSER_ID).sudo().with_company(company_id).create( moves = self.env['stock.move'].with_user(SUPERUSER_ID).sudo().with_company(company_id).create(moves_values)
moves_values)
# Since action_confirm launch following procurement_group we should activate it. # Since action_confirm launch following procurement_group we should activate it.
moves._action_confirm() moves._action_confirm()
return True return True
@api.model @api.model
@@ -217,23 +217,6 @@ class StockRule(models.Model):
( (
p.move_dest_ids.procure_method != 'make_to_order' and not p.move_dest_ids.procure_method != 'make_to_order' and not
p.move_raw_ids and not p.workorder_ids)).action_confirm() p.move_raw_ids and not p.workorder_ids)).action_confirm()
# 处理 根据制造订单生成的采购单坯料入库时到原材料库,手动将原材料位置该为坯料存货区
for production in productions:
if production.picking_ids:
product_type_id = production.picking_ids[0].move_ids[0].product_id.categ_id
if product_type_id.name == '坯料':
location_id = self.env['stock.location'].search([('name', '=', '坯料存货区')])
if not location_id:
logging.info(f'没有搜索到【坯料存货区】: {location_id}')
break
for picking_id in production.picking_ids:
if picking_id.picking_type_id.name == '内部调拨':
if picking_id.location_dest_id.product_type != product_type_id:
picking_id.location_dest_id = location_id.id
elif picking_id.picking_type_id.name == '生产发料':
if picking_id.location_id.product_type != product_type_id:
picking_id.location_id = location_id.id
for production in productions: for production in productions:
''' '''
创建制造订单时生成序列号 创建制造订单时生成序列号
@@ -288,70 +271,14 @@ 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(
[('product_id.id', '=', production_item.product_id.id),
('origin', '=', production_item.origin)],
limit=1, order='id asc')
if production_item.product_id.id in product_id_to_production_names: 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:
is_purchase = False
sorted_process_parameters = sorted(production_item.product_id.model_process_parameters_ids,
key=lambda w: w.id)
consecutive_process_parameters = []
m = 0
for i in range(len(sorted_process_parameters) - 1):
if m == 0:
is_purchase = False
if self.env['product.template']._get_process_parameters_product(
sorted_process_parameters[i]).partner_id == self.env[
'product.template']._get_process_parameters_product(sorted_process_parameters[
i + 1]).partner_id and \
sorted_process_parameters[i].gain_way == '外协':
if sorted_process_parameters[i] not in consecutive_process_parameters:
consecutive_process_parameters.append(sorted_process_parameters[i])
consecutive_process_parameters.append(sorted_process_parameters[i + 1])
m += 1
continue
else:
if m == len(consecutive_process_parameters) - 1 and m != 0:
self.env['purchase.order'].get_purchase_order(consecutive_process_parameters,
production_item,
product_id_to_production_names)
if sorted_process_parameters[i] in consecutive_process_parameters:
is_purchase = True
consecutive_process_parameters = []
m = 0
# 当前面的连续外协采购单生成再生成当前外协采购单
if is_purchase is False:
self.env['purchase.order'].get_purchase_order(consecutive_process_parameters,
production_item,
product_id_to_production_names)
if m == len(consecutive_process_parameters) - 1 and m != 0:
self.env['purchase.order'].get_purchase_order(consecutive_process_parameters,
production_item,
product_id_to_production_names)
if sorted_process_parameters[i] in consecutive_process_parameters:
is_purchase = True
consecutive_process_parameters = []
m = 0
if m == len(consecutive_process_parameters) - 1 and m != 0:
self.env['purchase.order'].get_purchase_order(consecutive_process_parameters,
production_item,
product_id_to_production_names)
if is_purchase is False and m == 0:
if len(sorted_process_parameters) == 1:
self.env['purchase.order'].get_purchase_order(sorted_process_parameters,
production_item,
product_id_to_production_names)
else:
self.env['purchase.order'].get_purchase_order(sorted_process_parameters[i],
production_item,
product_id_to_production_names)
# # 同一个产品多个制造订单对应一个编程单和模型库 # # 同一个产品多个制造订单对应一个编程单和模型库
# # 只调用一次fetchCNC并将所有生产订单的名称作为字符串传递 # # 只调用一次fetchCNC并将所有生产订单的名称作为字符串传递
if not production_item.programming_no: if not production_item.programming_no:
production_programming = self.env['mrp.production'].search(
[('product_id.id', '=', production_item.product_id.id),
('origin', '=', production_item.origin)],
limit=1, order='id asc')
if 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]))
@@ -441,7 +368,7 @@ class ProductionLot(models.Model):
if product.tracking == "serial": if product.tracking == "serial":
last_serial = self.env['stock.lot'].search( last_serial = self.env['stock.lot'].search(
[('company_id', '=', company.id), ('product_id', '=', product.id)], [('company_id', '=', company.id), ('product_id', '=', product.id)],
limit=1, order='name desc') limit=1, order='id DESC')
if last_serial: if last_serial:
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)
@@ -541,11 +468,12 @@ class ProductionLot(models.Model):
class StockPicking(models.Model): class StockPicking(models.Model):
_inherit = 'stock.picking' _inherit = 'stock.picking'
surface_technics_parameters_id = fields.Many2one('sf.production.process.parameter', string="表面工艺可选参数") # workorder_in_id = fields.One2many('mrp.workorder', 'picking_in_id')
# workorder_out_id = fields.One2many('mrp.workorder', 'picking_out_id')
# 设置外协出入单的名称 # 设置外协出入单的名称
def _get_name_Res(self, rescode): def _get_name_Res(self, rescode):
last_picking = self.sudo().search([('name', 'ilike', rescode)], order='create_date desc,id desc', limit=1) last_picking = self.sudo().search([('name', 'like', rescode)], order='create_date desc,id desc', limit=1)
if not last_picking: if not last_picking:
num = "%04d" % 1 num = "%04d" % 1
else: else:
@@ -571,7 +499,7 @@ class StockPicking(models.Model):
[('barcode', 'ilike', 'WH-PREPRODUCTION')]).id), [('barcode', 'ilike', 'WH-PREPRODUCTION')]).id),
('location_id', '=', self.env['stock.location'].search( ('location_id', '=', self.env['stock.location'].search(
[('barcode', 'ilike', 'VL-SPOC')]).id), [('barcode', 'ilike', 'VL-SPOC')]).id),
('origin', '=', self.origin), ('picking_id', '=', self.id)]) ('origin', '=', self.origin)])
if self.location_id == move_in.location_id and self.location_dest_id == move_in.location_dest_id: if self.location_id == move_in.location_id and self.location_dest_id == move_in.location_dest_id:
if move_out.origin == move_in.origin: if move_out.origin == move_in.origin:
if move_out.picking_id.state != 'done': if move_out.picking_id.state != 'done':
@@ -588,7 +516,7 @@ class StockPicking(models.Model):
[('barcode', 'ilike', 'VL-SPOC')]).id), [('barcode', 'ilike', 'VL-SPOC')]).id),
('origin', '=', self.origin)]) ('origin', '=', self.origin)])
production = self.env['mrp.production'].search([('name', '=', self.origin)]) production = self.env['mrp.production'].search([('name', '=', self.origin)])
if move_in.state != 'done': if move_in:
move_in.write({'state': 'assigned'}) move_in.write({'state': 'assigned'})
self.env['stock.move.line'].create(move_in.get_move_line(production, None)) self.env['stock.move.line'].create(move_in.get_move_line(production, None))
@@ -598,7 +526,7 @@ class StockPicking(models.Model):
def create_outcontract_picking(self, sorted_workorders_arr, item): def create_outcontract_picking(self, sorted_workorders_arr, item):
m = 0 m = 0
for sorted_workorders in sorted_workorders_arr: for sorted_workorders in sorted_workorders_arr:
# pick_ids = [] pick_ids = []
if m == 0: if m == 0:
outcontract_stock_move = self.env['stock.move'].search( outcontract_stock_move = self.env['stock.move'].search(
[('workorder_id', '=', sorted_workorders.id), ('production_id', '=', item.id)]) [('workorder_id', '=', sorted_workorders.id), ('production_id', '=', item.id)])
@@ -617,7 +545,7 @@ class StockPicking(models.Model):
outcontract_picking_type_out)) outcontract_picking_type_out))
picking_out = self.create( picking_out = self.create(
moves_out._get_new_picking_values_Res(item, sorted_workorders, 'WH/OCOUT/')) moves_out._get_new_picking_values_Res(item, sorted_workorders, 'WH/OCOUT/'))
# pick_ids.append(picking_out.id) pick_ids.append(picking_out.id)
moves_out.write( moves_out.write(
{'picking_id': picking_out.id, 'state': 'waiting', 'workorder_id': sorted_workorders.id}) {'picking_id': picking_out.id, 'state': 'waiting', 'workorder_id': sorted_workorders.id})
moves_out._assign_picking_post_process(new=new_picking) moves_out._assign_picking_post_process(new=new_picking)
@@ -626,12 +554,12 @@ class StockPicking(models.Model):
outcontract_picking_type_in)) outcontract_picking_type_in))
picking_in = self.create( picking_in = self.create(
moves_in._get_new_picking_values_Res(item, sorted_workorders, 'WH/OCIN/')) moves_in._get_new_picking_values_Res(item, sorted_workorders, 'WH/OCIN/'))
# pick_ids.append(picking_in.id) pick_ids.append(picking_in.id)
moves_in.write( moves_in.write(
{'picking_id': picking_in.id, 'state': 'waiting', 'workorder_id': sorted_workorders.id}) {'picking_id': picking_in.id, 'state': 'waiting', 'workorder_id': sorted_workorders.id})
moves_in._assign_picking_post_process(new=new_picking) moves_in._assign_picking_post_process(new=new_picking)
m += 1 m += 1
# sorted_workorders.write({'picking_ids': [(6, 0, pick_ids)]}) sorted_workorders.write({'picking_ids': [(6, 0, pick_ids)]})
class ReStockMove(models.Model): class ReStockMove(models.Model):
@@ -662,7 +590,6 @@ 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,
'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',

View File

@@ -150,12 +150,5 @@ access_sf_processing_panel_group_sf_order_user,sf_processing_panel_group_sf_orde
access_sf_production_wizard_group_sf_order_user,sf_production_wizard_group_sf_order_user,model_sf_production_wizard,sf_base.group_sf_order_user,1,1,1,0 access_sf_production_wizard_group_sf_order_user,sf_production_wizard_group_sf_order_user,model_sf_production_wizard,sf_base.group_sf_order_user,1,1,1,0
access_sf_processing_panel_group_plan_dispatch,sf_processing_panel_group_plan_dispatch,model_sf_processing_panel,sf_base.group_plan_dispatch,1,1,1,0 access_sf_processing_panel_group_plan_dispatch,sf_processing_panel_group_plan_dispatch,model_sf_processing_panel,sf_base.group_plan_dispatch,1,1,1,0
access_sf_agv_scheduling_admin,sf_agv_scheduling_admin,model_sf_agv_scheduling,base.group_system,1,1,1,1
access_sf_agv_scheduling_group_sf_order_user,sf_agv_scheduling_group_sf_order_user,model_sf_agv_scheduling,sf_base.group_sf_order_user,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
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
150
151
152
153
154

View File

@@ -1,36 +1,16 @@
var RFID = '' var RFID = ''
$(document).off('keydown') $(document).off('keydown')
$(document).on('keydown', 'body.o_web_client', function (e) { console.log(2222)
$(document).on('keydown', '.modal.d-block.o_technical_modal,body.o_web_client', function (e) {
const dom = $('.customRFID')
if(!dom.length) return
setTimeout(() => { setTimeout(() => {
RFID = '' RFID = ''
}, 200) }, 200)
if(e.key == 'Enter' && e.keyCode == 13 || e.key == 'Tab' && e.keyCode == 9){ if(e.key == 'Enter' && e.keyCode == 13 || e.key == 'Tab' && e.keyCode == 9){
if(!RFID || RFID.length <= 3) return;
let fieldValue1 = $('[name="routing_type"]'); dom.children('span').text(RFID)
console.log('字段值:', fieldValue1.text()); RFID = ''
console.log(RFID)
let fieldValue2 = $('[name="rfid_code"]');
console.log('字段值2:', fieldValue2.text());
// if(!RFID || RFID.length <= 3) return;
// $('[name="button_start"]').trigger('click')
// setTimeout(() => {
// $('.o_dialog .modal-footer .btn-primary').trigger('click')
// }, 50)
// RFID = ''
// return;
// fieldValue2.val() === '')
// 检查字段值是否等于“装夹预调”
if (fieldValue1.text() === '装夹预调') {
if (!RFID || RFID.length <= 3) return;
$('[name="button_start"]').trigger('click');
setTimeout(() => {
$('.o_dialog .modal-footer .btn-primary').trigger('click');
}, 100);
}
RFID = '';
return; return;
} }
RFID += e.key RFID += e.key

View File

@@ -1,49 +0,0 @@
odoo.define('sf_manufacturing.action_dispatch_confirm', function (require) {
const core = require('web.core');
const ajax = require('web.ajax');
const Dialog = require('web.Dialog');
var rpc = require('web.rpc');
var _t = core._t;
async function dispatch_confirm(parent, {params}) {
const dialog = new Dialog(parent, {
title: "确认",
$content: $('<div>').append("请确认是否仅配送" + params.workorder_count + "个工件?"),
buttons: [
{ text: "确认", classes: 'btn-primary', close: true, click: () => dispatchConfirmed(parent, params) },
{ text: "取消", close: true },
],
});
dialog.open();
async function dispatchConfirmed(parent, params) {
console.log(parent, 'parent')
rpc.query({
model: 'sf.workpiece.delivery.wizard',
method: 'confirm',
args: [params.active_id]
,
kwargs: {
context: params.context,
}
}).then(res => {
parent.services.action.doAction({
'type': 'ir.actions.client',
'tag': 'display_notification',
'target': 'new',
'params': {
'message': '任务下发成功AGV任务调度编号为【' + res.name + '】',
'type': 'success',
'sticky': false,
'next': {'type': 'ir.actions.act_window_close'},
}
});
})
}
}
core.action_registry.add('dispatch_confirm', dispatch_confirm);
return dispatch_confirm;
});

View File

@@ -1,57 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<t t-name="sf_manufacturing.button_show" t-inherit="web.ListView.Buttons" t-inherit-mode="extension" owl="1">
<xpath expr="//div/t[@t-if='nbSelected']" position="after">
<t t-elif="!nbSelected">
<t t-foreach="archInfo.headerButtons" t-as="button" t-key="button.id">
<t t-if="button.modifiers.force_show">
<ListViewHeaderButton
list="model.root"
clickParams="button.clickParams"
defaultRank="button.defaultRank"
domain="props.domain"
icon="button.icon"
string="button.string"
title="button.title"
className="button.className+' ms-2'"
/>
</t>
</t>
</t>
</xpath>
<xpath expr="//div/t[@t-if='nbSelected']" position="replace">
<t t-if="nbSelected">
<t t-foreach="archInfo.headerButtons" t-as="button" t-key="button.id">
<t t-if="!button.modifiers.force_show">
<ListViewHeaderButton
list="model.root"
clickParams="button.clickParams"
defaultRank="button.defaultRank"
domain="props.domain"
icon="button.icon"
string="button.string"
title="button.title"
/>
</t>
</t>
<t t-if="!env.isSmall">
<t t-call="web.ListView.Selection"/>
</t>
<t t-foreach="archInfo.headerButtons" t-as="button" t-key="button.id">
<t t-if="button.modifiers.force_show == 1">
<ListViewHeaderButton
list="model.root"
clickParams="button.clickParams"
defaultRank="button.defaultRank"
domain="props.domain"
icon="button.icon"
string="button.string"
title="button.title"
className="button.className+' ms-2'"
/>
</t>
</t>
</t>
</xpath>
</t>
</templates>

View File

@@ -1,79 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<!-- agv站点 -->
<record id="view_agv_scheduling_tree" model="ir.ui.view">
<field name="name">agv调度</field>
<field name="model">sf.agv.scheduling</field>
<field name="arch" type="xml">
<tree editable="bottom" delete="0" create="0">
<field name="state" widget="badge"
decoration-success="state == '已配送'"
decoration-warning="state == '待下发'"
decoration-danger="state == '配送中'"
decoration-info="state == '已取消'"
/>
<field name="agv_route_type" invisible="1"/>
<field name="name"/>
<field name="agv_route_id"/>
<field name="start_site_id"/>
<field name="end_site_id"/>
<field name="site_state"/>
<field name="delivery_workpieces"/>
<field name="task_create_time" readonly="1"/>
<field name="task_delivery_time" readonly="1"/>
<field name="task_completion_time" readonly="1"/>
<field name="task_duration" readonly="1"/>
<button
name="button_cancel"
string="取消" type="object"
attrs="{'invisible': ['|', ('state', '!=', '待下发'), ('agv_route_type', '=', '运送空料架')]}"
icon="fa-times"
class="btn-danger"
confirm="你确定要取消这条记录吗?"
/>
</tree>
</field>
</record>
<record id="view_agv_scheduling_search" model="ir.ui.view">
<field name="name">sf.agv.scheduling.search</field>
<field name="model">sf.agv.scheduling</field>
<field name="arch" type="xml">
<search string="AGV调度">
<field name="name"/>
<field name="agv_route_id"/>
<field name="start_site_id"/>
<field name="end_site_id"/>
<field name="delivery_workpieces"/>
<field name="state" string="状态"/>
<filter name="filter_to_be_issued" string="待下发" domain="[('state', 'in', ['待下发'])]"/>
<filter name="filter_delivering" string="配送中" domain="[('state', 'in', ['配送中'])]"/>
<filter name="filter_delivered" string="已配送" domain="[('state', 'in', ['已配送'])]"/>
</search>
</field>
</record>
<record id="action_agv_scheduling_tree" model="ir.actions.act_window">
<field name="name">AGV调度</field>
<field name="res_model">sf.agv.scheduling</field>
<field name="view_mode">tree</field>
<field name="context">
{
"search_default_filter_to_be_issued": 1,
"search_default_filter_delivering": 1,
}
</field>
</record>
<menuitem
id="menu_action_agv_scheduling"
name="AGV调度"
sequence="28"
action="action_agv_scheduling_tree"
parent="mrp.menu_mrp_manufacturing"
groups="sf_base.group_sf_order_user,sf_base.group_sf_mrp_manager,sf_base.group_sf_equipment_user"
/>
</data>
</odoo>

View File

@@ -8,7 +8,7 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree editable="bottom"> <tree editable="bottom">
<field name="name" required="1" attrs="{'readonly': [('id', '!=', False)]}"/> <field name="name" required="1" attrs="{'readonly': [('id', '!=', False)]}"/>
<field name="workcenter_id" required="1" options="{'no_create': True}"/> <field name="owning_region" required="1" attrs="{'readonly': [('id', '!=', False)]}"/>
<field name="state" required="1" attrs="{'readonly': [('id', '!=', False)]}"/> <field name="state" required="1" attrs="{'readonly': [('id', '!=', False)]}"/>
<field name="divide_the_work" required="1"/> <field name="divide_the_work" required="1"/>
</tree> </tree>
@@ -40,9 +40,8 @@
<field name="start_site_id" required="1" options="{'no_create': True}" string="起点接驳站" <field name="start_site_id" required="1" options="{'no_create': True}" string="起点接驳站"
attrs="{'readonly': [('id', '!=', False)]}"/> attrs="{'readonly': [('id', '!=', False)]}"/>
<field name="end_site_id" required="1" options="{'no_create': True}" string="终点接驳站"/> <field name="end_site_id" required="1" options="{'no_create': True}" string="终点接驳站"/>
<!-- <field name="destination_production_line_id" required="1" options="{'no_create': True}"--> <field name="destination_production_line_id" required="1" options="{'no_create': True}"
<!-- attrs="{'readonly': [('id', '!=', False)]}"/>--> attrs="{'readonly': [('id', '!=', False)]}"/>
<field name="workcenter_id"/>
</tree> </tree>
</field> </field>
</record> </record>

View File

@@ -7,10 +7,10 @@
<field name="model">mrp.production</field> <field name="model">mrp.production</field>
<field name="inherit_id" ref="mrp.mrp_production_tree_view"/> <field name="inherit_id" ref="mrp.mrp_production_tree_view"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<!-- <xpath expr="//button[@name='do_unreserve']" position="after">--> <!-- <xpath expr="//button[@name='do_unreserve']" position="after">-->
<!-- <button name="do_update_program" type="object" string="更新程序"--> <!-- <button name="do_update_program" type="object" string="更新程序"-->
<!-- groups="sf_base.group_sf_mrp_user"/>--> <!-- groups="sf_base.group_sf_mrp_user"/>-->
<!-- </xpath>--> <!-- </xpath>-->
<xpath expr="//field[@name='product_id']" position="replace"/> <xpath expr="//field[@name='product_id']" position="replace"/>
<xpath expr="//field[@name='product_qty']" position="replace"/> <xpath expr="//field[@name='product_qty']" position="replace"/>
<xpath expr="//field[@name='product_uom_id']" position="replace"/> <xpath expr="//field[@name='product_uom_id']" position="replace"/>
@@ -70,7 +70,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 confirmed,pending_cam,progress,done
</attribute> </attribute>
</xpath> </xpath>
<xpath expr="//sheet//group//group[2]//label" position="before"> <xpath expr="//sheet//group//group[2]//label" position="before">
@@ -127,10 +127,10 @@
confirm="是否确认更新程序" confirm="是否确认更新程序"
attrs="{'invisible': ['|',('state', '!=', 'rework'),('programming_state', '!=', '已编程未下发')]}"/> attrs="{'invisible': ['|',('state', '!=', 'rework'),('programming_state', '!=', '已编程未下发')]}"/>
<button name="button_rework" string="返工" type="object" groups="sf_base.group_sf_mrp_user" <button name="button_rework" string="返工" type="object" groups="sf_base.group_sf_mrp_user"
attrs="{'invisible': ['|','|',('state', '!=', 'rework') ,('programming_state', '!=', '已编程'),('is_rework', '=', True)]}"/> attrs="{'invisible': ['|',('state', '!=', 'rework') ,('programming_state', '!=', '已编程')]}"/>
<button name="button_scrap_new" string="报废" type="object" <!-- <button name="%(sf_manufacturing.action_sf_production_wizard)d" string="报废" type="action"-->
groups="sf_base.group_sf_mrp_user" <!-- groups="sf_base.group_sf_mrp_user"-->
attrs="{'invisible': ['|',('is_scrap', '=', False),('state','=','cancel')]}"/> <!-- attrs="{'invisible': [('is_scrap', '=', False)]}"/>-->
</xpath> </xpath>
<xpath expr="(//header//button[@name='button_mark_done'])[3]" position="replace"> <xpath expr="(//header//button[@name='button_mark_done'])[3]" position="replace">
<button name="button_mark_done" attrs="{'invisible': [ <button name="button_mark_done" attrs="{'invisible': [
@@ -201,19 +201,6 @@
data-hotkey="l"/> data-hotkey="l"/>
</xpath> </xpath>
<xpath expr="//button[@name='action_view_mo_delivery']" position="before">
<button class="oe_stat_button" name="action_view_remanufacture_productions" type="object"
icon="fa-wrench" attrs="{'invisible': [('remanufacture_count', '=', 0)]}"
groups="mrp.group_mrp_user">
<div class="o_field_widget o_stat_info">
<span class="o_stat_value">
<field name="remanufacture_count"/>
</span>
<span class="o_stat_text">新的制造</span>
</div>
</button>
</xpath>
<xpath expr="//header//button[@name='action_toggle_is_locked']" position="replace"> <xpath expr="//header//button[@name='action_toggle_is_locked']" position="replace">
<button name="action_toggle_is_locked" <button name="action_toggle_is_locked"
attrs="{'invisible': ['|', ('show_lock', '=', False), ('is_locked', '=', True)]}" attrs="{'invisible': ['|', ('show_lock', '=', False), ('is_locked', '=', True)]}"
@@ -298,7 +285,7 @@
<xpath expr="//sheet//notebook//page[@name='operations']" position="after"> <xpath expr="//sheet//notebook//page[@name='operations']" position="after">
<page string="检测结果" attrs="{'invisible': [('detection_result_ids', '=', [])]}"> <page string="检测结果" attrs="{'invisible': [('detection_result_ids', '=', [])]}">
<field name="detection_result_ids" string="" readonly="0"> <field name="detection_result_ids" string="" readonly="1">
<tree sample="1"> <tree sample="1">
<field name="production_id" invisible="1"/> <field name="production_id" invisible="1"/>
<field name="processing_panel"/> <field name="processing_panel"/>
@@ -437,12 +424,6 @@
<xpath expr="//header//button[@name='action_cancel']" position="replace"> <xpath expr="//header//button[@name='action_cancel']" position="replace">
<button name="action_cancel" type="object" string="取消" groups="sf_base.group_sf_mrp_user"/> <button name="action_cancel" type="object" string="取消" groups="sf_base.group_sf_mrp_user"/>
</xpath> </xpath>
<xpath expr="//field[@name='state']" position="replace">
<field name="state" decoration-success="state in ('done', 'to_close')"
decoration-warning="state == 'progress'" decoration-info="state == 'confirmed'"
decoration-danger="state in ('cancel','rework','scrap')" decoration-muted="state == 'draft'"
optional="show" widget="badge" class="text-dark"/>
</xpath>
<xpath expr="//field[@name='state']" position="after"> <xpath expr="//field[@name='state']" position="after">
<field name="tool_state" invisible="1"/> <field name="tool_state" invisible="1"/>
</xpath> </xpath>
@@ -479,7 +460,6 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<xpath expr="//filter[@name='filter_in_progress']" position="before"> <xpath expr="//filter[@name='filter_in_progress']" position="before">
<filter string="返工" name="filter_rework" domain="[('state', '=', 'rework')]"/> <filter string="返工" name="filter_rework" domain="[('state', '=', 'rework')]"/>
<filter string="报废" name="filter_scrap" domain="[('state', '=', 'scrap')]"/>
</xpath> </xpath>
<xpath expr="//filter[@name='planning_issues']" position="before"> <xpath expr="//filter[@name='planning_issues']" position="before">
<separator/> <separator/>
@@ -588,7 +568,7 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<form> <form>
<group> <group>
<field name="handle_result"/> <!-- <field name="handle_result"/>-->
<field name="test_report" readonly="1" widget="pdf_viewer"/> <field name="test_report" readonly="1" widget="pdf_viewer"/>
</group> </group>
</form> </form>

View File

@@ -17,7 +17,6 @@
<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="is_repeat"/> <field name="is_repeat"/>
<field name="reserved_duration"/>
</field> </field>
</field> </field>
</record> </record>

View File

@@ -182,7 +182,6 @@
</xpath> </xpath>
<xpath expr="//field[@name='resource_calendar_id']" position="after"> <xpath expr="//field[@name='resource_calendar_id']" position="after">
<field name="is_process_outsourcing"/> <field name="is_process_outsourcing"/>
<field name="is_agv_scheduling"/>
</xpath> </xpath>
</field> </field>
</record> </record>

View File

@@ -10,7 +10,7 @@
<field name="name" decoration-success="is_subcontract" decoration-bf="is_subcontract"/> <field name="name" decoration-success="is_subcontract" decoration-bf="is_subcontract"/>
</field> </field>
<field name="name" position="before"> <field name="name" position="before">
<field name="sequence" string="序号"/> <field name="sequence"/>
<field name='user_permissions' invisible="1"/> <field name='user_permissions' invisible="1"/>
</field> </field>
<field name="name" position="after"> <field name="name" position="after">
@@ -36,22 +36,14 @@
<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"/>
</xpath> </xpath>
<xpath expr="//field[@name='date_planned_start']" position="before">
<field name="reserved_duration" string="计划预留时间" optional="show"/>
</xpath>
<xpath expr="//field[@name='date_planned_finished']" position="replace"> <xpath expr="//field[@name='date_planned_finished']" position="replace">
<field name="date_planned_finished" string="计划结束日期" optional="hide"/> <field name="date_planned_finished" string="计划结束日期" optional="hide"/>
</xpath> </xpath>
<xpath expr="//button[@name='button_start']" position="attributes"> <xpath expr="//button[@name='button_start']" position="attributes">
<!-- <attribute name="attrs">{'invisible': ['|', '|', '|','|','|', ('production_state','in', ('draft',-->
<!-- 'done',-->
<!-- 'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel')),-->
<!-- ('is_user_working', '!=', False),("user_permissions","=",False),("name","=","CNC加工")]}-->
<!-- </attribute>-->
<attribute name="attrs">{'invisible': ['|', '|', '|','|','|', ('production_state','in', ('draft', <attribute name="attrs">{'invisible': ['|', '|', '|','|','|', ('production_state','in', ('draft',
'done', 'done',
'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel')), 'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel')),
('is_user_working', '!=', False),("user_permissions","=",False),("name","in",("CNC加工","解除装夹"))]} ('is_user_working', '!=', False),("user_permissions","=",False),("name","=","CNC加工")]}
</attribute> </attribute>
</xpath> </xpath>
<xpath expr="//button[@name='%(mrp.act_mrp_block_workcenter_wo)d']" position="attributes"> <xpath expr="//button[@name='%(mrp.act_mrp_block_workcenter_wo)d']" position="attributes">
@@ -121,25 +113,11 @@
<field name="model">mrp.workorder</field> <field name="model">mrp.workorder</field>
<field name="inherit_id" ref="mrp.mrp_production_workorder_form_view_inherit"/> <field name="inherit_id" ref="mrp.mrp_production_workorder_form_view_inherit"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<xpath expr="//form" position="inside">
<script src="sf_manufacturing/static/src/js/customRFID.js"/>
</xpath>
<xpath expr="//header/field[@name='state']" position="replace"> <xpath expr="//header/field[@name='state']" position="replace">
<field name="state" widget="statusbar" <field name="state" widget="statusbar"
statusbar_visible="pending,waiting,ready,progress,to be detected,done,rework"/> statusbar_visible="pending,waiting,ready,progress,to be detected,done,rework"/>
</xpath> </xpath>
<xpath expr="//div[@name='button_box']" position="inside"> <xpath expr="//div[@name='button_box']" position="inside">
<button type="object" name="action_view_surface_technics_purchase" class="oe_stat_button"
icon="fa-credit-card"
groups="base.group_user,sf_base.group_sf_order_user"
attrs="{'invisible': [('surface_technics_purchase_count', '=', 0),('routing_type', '!=', '表面工艺')]}">
<div class="o_field_widget o_stat_info">
<span class="o_stat_value">
<field name="surface_technics_purchase_count"/>
</span>
<span class="o_stat_text">采购</span>
</div>
</button>
<button type="object" name="action_view_surface_technics_picking" class="oe_stat_button" icon="fa-truck" <button type="object" name="action_view_surface_technics_picking" class="oe_stat_button" icon="fa-truck"
groups="base.group_user,sf_base.group_sf_order_user" groups="base.group_user,sf_base.group_sf_order_user"
attrs="{'invisible': [('surface_technics_picking_count', '=', 0)]}"> attrs="{'invisible': [('surface_technics_picking_count', '=', 0)]}">
@@ -152,7 +130,6 @@
<field name='name' invisible="1"/> <field name='name' invisible="1"/>
<field name='is_rework' invisible="1"/> <field name='is_rework' invisible="1"/>
<field name='is_delivery' invisible="1"/> <field name='is_delivery' invisible="1"/>
<field name="is_trayed" invisible="1"/>
<!-- <field name='is_send_program_again' invisible="1"/>--> <!-- <field name='is_send_program_again' invisible="1"/>-->
<!-- 工单form页面的开始停工按钮等 --> <!-- 工单form页面的开始停工按钮等 -->
<!-- <button name="button_start" type="object" string="开始" class="btn-success" --> <!-- <button name="button_start" type="object" string="开始" class="btn-success" -->
@@ -165,12 +142,8 @@
<!-- attrs="{'invisible': ['|', '|', ('production_state', 'not in', ('pending_processing', 'pending_cam', 'pending_era_cam')), ('state','!=','progress'), ('routing_type', 'not in', ('装夹预调', 'CNC加工', '解除装夹'))]}" --> <!-- attrs="{'invisible': ['|', '|', ('production_state', 'not in', ('pending_processing', 'pending_cam', 'pending_era_cam')), ('state','!=','progress'), ('routing_type', 'not in', ('装夹预调', 'CNC加工', '解除装夹'))]}" -->
<!-- groups="sf_base.group_sf_mrp_user" confirm="是否确认完工"/> --> <!-- groups="sf_base.group_sf_mrp_user" confirm="是否确认完工"/> -->
<!-- <button name="button_start" type="object" string="开始" class="btn-success" confirm="是否确认开始"-->
<!-- attrs="{'invisible': ['|', '|', '|', ('production_state','in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel','to be detected')), ('is_user_working', '!=', False)]}"/>-->
<button name="button_start" type="object" string="开始" class="btn-success" confirm="是否确认开始" <button name="button_start" type="object" string="开始" class="btn-success" confirm="是否确认开始"
attrs="{'invisible': ['|', '|', '|', '|', '|', ('routing_type', '=', '装夹预调'), ('routing_type', '=', '解除装夹'), ('production_state','in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel','to be detected')), ('is_user_working', '!=', False)]}"/> attrs="{'invisible': ['|', '|', '|', ('production_state','in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel','to be detected')), ('is_user_working', '!=', False)]}"/>
<button name="button_start" type="object" string="开始" class="btn-success"
attrs="{'invisible': ['|', '|', '|', '|', ('routing_type', '!=', '装夹预调'), ('production_state','in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel','to be detected')), ('is_user_working', '!=', False)]}"/>
<button name="button_pending" type="object" string="暂停" class="btn-warning" <button name="button_pending" type="object" string="暂停" class="btn-warning"
attrs="{'invisible': ['|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('is_user_working', '=', False)]}"/> attrs="{'invisible': ['|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('is_user_working', '=', False)]}"/>
<button name="button_finish" type="object" string="完成" class="btn-success" confirm="是否确认完工" <button name="button_finish" type="object" string="完成" class="btn-success" confirm="是否确认完工"
@@ -191,14 +164,11 @@
<!-- context="{'default_workcenter_id': workcenter_id}" class="btn-danger" --> <!-- context="{'default_workcenter_id': workcenter_id}" class="btn-danger" -->
<!-- groups="sf_base.group_sf_mrp_user" --> <!-- groups="sf_base.group_sf_mrp_user" -->
<!-- attrs="{'invisible': ['|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '!=', 'blocked'),('state','=','done')]}"/> --> <!-- attrs="{'invisible': ['|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '!=', 'blocked'),('state','=','done')]}"/> -->
<!-- <button name="button_workpiece_delivery" type="object" string="工件配送" class="btn-primary"--> <button name="button_workpiece_delivery" type="object" string="工件配送" class="btn-primary"
<!-- attrs="{'invisible': ['|','|','|','|',('routing_type','!=','装夹预调'),('is_delivery','=',True),('state','!=','done'),('is_rework','=',True),'&amp;',('rfid_code','in',['',False]),('state','=','done')]}"/>--> attrs="{'invisible': ['|','|','|','|',('routing_type','!=','装夹预调'),('is_delivery','=',True),('state','!=','done'),('is_rework','=',True),'&amp;',('rfid_code','in',['',False]),('state','=','done')]}"/>
<button name="button_rework_pre" type="object" string="返工" <button name="button_rework_pre" type="object" string="返工"
class="btn-primary" class="btn-primary"
attrs="{'invisible': ['|','|',('routing_type','!=','装夹预调'),('state','!=','progress'),('is_rework','=',True)]}"/> attrs="{'invisible': ['|','|',('routing_type','!=','装夹预调'),('state','!=','progress'),('is_rework','=',True)]}"/>
<button name="unbind_tray" type="object" string="解绑托盘"
class="btn-primary"
attrs="{'invisible': ['|', '|', '|', ('routing_type','!=','装夹预调'),('state','!=','progress'), ('is_trayed', '=', False), ('state', 'in', ('done'))]}"/>
<button name="print_method" type="object" string="打印二维码" class="btn-primary" <button name="print_method" type="object" string="打印二维码" class="btn-primary"
attrs="{'invisible': ['|',('routing_type','!=','解除装夹'),('state','!=','done')]}"/> attrs="{'invisible': ['|',('routing_type','!=','解除装夹'),('state','!=','done')]}"/>
</xpath> </xpath>
@@ -541,7 +511,7 @@
<xpath expr="//page[1]" position="before"> <xpath expr="//page[1]" position="before">
<page string="CNC程序" attrs='{"invisible": [("routing_type","!=","CNC加工")]}'> <page string="CNC程序" attrs='{"invisible": [("routing_type","!=","CNC加工")]}'>
<field name="cnc_ids" widget="one2many" string="工作程序" default_order="sequence_number,id" <field name="cnc_ids" widget="one2many" string="工作程序" default_order="sequence_number,id"
readonly="0"> readonly="1">
<tree> <tree>
<field name="sequence_number"/> <field name="sequence_number"/>
<field name="program_name"/> <field name="program_name"/>
@@ -661,27 +631,27 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree string="工件配送" class="center" create="0" delete="0"> <tree string="工件配送" class="center" create="0" delete="0">
<header> <header>
<button name="button_delivery" type="object" string="工件配送" class="btn-primary" attrs="{'force_show':1}"/> <button name="button_delivery" type="object" string="配送" class="oe_highlight"/>
</header> </header>
<field name="status" widget="badge" <field name="status" widget="badge"
decoration-success="status == '已配送'" decoration-success="status == '已配送'"
decoration-warning="status == '待下发'" decoration-warning="status == '待下发'"
decoration-danger="status == '已下发'" decoration-danger="status == '待配送'"
decoration-info="status == '已取消'" decoration-info="status == '已取消'"
/> />
<field name="name"/> <field name="name"/>
<field name="production_id"/> <field name="production_id"/>
<field name="type" readonly="1"/> <field name="type" readonly="1"/>
<field name="production_line_id" options="{'no_create': True}" readonly="1"/> <field name="production_line_id" options="{'no_create': True}" readonly="1"/>
<!-- <field name="route_id" options="{'no_create': True}"--> <field name="route_id" options="{'no_create': True}"
<!-- domain="[('route_type','in',['上产线','下产线'])]"/>--> domain="[('route_type','in',['上产线','下产线'])]"/>
<field name="feeder_station_start_id" readonly="1" force_save="1"/> <field name="feeder_station_start_id" readonly="1" force_save="1"/>
<!-- <field name="feeder_station_destination_id" readonly="1" force_save="1"/>--> <field name="feeder_station_destination_id" readonly="1" force_save="1"/>
<field name="is_cnc_program_down" readonly="1"/> <field name="is_cnc_program_down" readonly="1"/>
<!-- <field name="rfid_code"/>--> <!-- <field name="rfid_code"/>-->
<!-- <field name="task_delivery_time" readonly="1"/>--> <field name="task_delivery_time" readonly="1"/>
<!-- <field name="task_completion_time" readonly="1"/>--> <field name="task_completion_time" readonly="1"/>
<!-- <field name="delivery_duration" widget="float_time"/>--> <field name="delivery_duration" widget="float_time"/>
</tree> </tree>
</field> </field>
</record> </record>
@@ -736,7 +706,7 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<search string="工件配送"> <search string="工件配送">
<filter name="filter_to_be_issued" string="待下发" domain="[('status', 'in', ['待下发'])]"/> <filter name="filter_to_be_issued" string="待下发" domain="[('status', 'in', ['待下发'])]"/>
<filter name="filter_issued" string="已下发" domain="[('status', 'in', ['已下发'])]"/> <filter name="filter_waiting_delivery" string="待配送" domain="[('status', 'in', ['待配送'])]"/>
<filter name="filter_delivered" string="已配送" domain="[('status', 'in', ['已配送'])]"/> <filter name="filter_delivered" string="已配送" domain="[('status', 'in', ['已配送'])]"/>
<field name="rfid_code"/> <field name="rfid_code"/>
<field name="production_id"/> <field name="production_id"/>
@@ -760,7 +730,7 @@
<field name="res_model">sf.workpiece.delivery</field> <field name="res_model">sf.workpiece.delivery</field>
<field name="search_view_id" ref="sf_workpiece_delivery_search"/> <field name="search_view_id" ref="sf_workpiece_delivery_search"/>
<field name="context">{'search_default_filter_to_be_issued': 1, <field name="context">{'search_default_filter_to_be_issued': 1,
'search_default_filter_issued': 1} 'search_default_filter_waiting_delivery': 1}
</field> </field>
<field name="view_mode">tree,form</field> <field name="view_mode">tree,form</field>
<field name="domain"> <field name="domain">
@@ -847,11 +817,5 @@
<field name="view_mode">tree</field> <field name="view_mode">tree</field>
<field name="domain">[('type','in',['运送空料架']),('name','not ilike','WDO')]</field> <field name="domain">[('type','in',['运送空料架']),('name','not ilike','WDO')]</field>
</record> </record>
<menuitem id="mrp.menu_mrp_manufacturing"
name="Operations"
sequence="10"
parent="mrp.menu_mrp_root"
groups="sf_base.group_sf_order_user,sf_base.group_sf_mrp_manager,sf_base.group_sf_equipment_user"/>
</odoo> </odoo>

View File

@@ -1,24 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="res_config_settings_view_form_sf_sync" model="ir.ui.view">
<field name="name">res.config.settings.view.form.inherit.sf_sync</field>
<field name="model">res.config.settings</field>
<field name="inherit_id" ref="base_setup.res_config_settings_view_form"/>
<field name="arch" type="xml">
<xpath expr="//div[@id='agv_config']/div" position="after">
<div class="col-12 col-lg-6 o_setting_box">
<div class="o_setting_left_pane">
<field name="is_agv_task_dispatch"/>
</div>
<div class="o_setting_right_pane">
<div class="text-muted">
<label for="is_agv_task_dispatch"/>
</div>
</div>
</div>
</xpath>
</field>
</record>
</data>
</odoo>

View File

@@ -2,8 +2,6 @@
# Part of YiZuo. See LICENSE file for full copyright and licensing details. # Part of YiZuo. See LICENSE file for full copyright and licensing details.
import logging import logging
from odoo.exceptions import UserError, ValidationError from odoo.exceptions import UserError, ValidationError
from collections import defaultdict, namedtuple
from odoo.addons.stock.models.stock_rule import ProcurementException
from datetime import datetime from datetime import datetime
from odoo import models, api, fields, _ from odoo import models, api, fields, _
@@ -13,91 +11,11 @@ class ProductionWizard(models.TransientModel):
_description = '制造订单向导' _description = '制造订单向导'
production_id = fields.Many2one('mrp.production', string='制造订单号') production_id = fields.Many2one('mrp.production', string='制造订单号')
reprogramming_num = fields.Integer('重新编程次数', default=0) is_reprogramming = fields.Boolean(string='申请重新编程', default=True)
is_reprogramming = fields.Boolean(string='申请重新编程', default=False)
is_remanufacture = fields.Boolean(string='重新生成制造订单', default=True) is_remanufacture = fields.Boolean(string='重新生成制造订单', default=True)
programming_states = fields.Selection(
[('待编程', '待编程'), ('编程中', '编程中'), ('已编程', '已编程'), ('已编程未下发', '已编程未下发'),
('已下发', '已下发')],
string='编程状态')
@api.onchange('is_remanufacture')
def _onchange_is_reprogramming(self):
if self.is_remanufacture is False:
self.is_reprogramming = False
def confirm(self): def confirm(self):
self.production_id.detection_result_ids.write({'handle_result': '已处理'}) if self.is_reprogramming is True:
self.production_id.write({'state': 'cancel', 'scrap_ids': [(0, 0, { self.production_id.update_programming_state()
'name': self.env['ir.sequence'].next_by_code('stock.scrap') or _('New'), self.production_id.action_cancel()
'product_id': self.production_id.product_id.id,
'scrap_qty': 1,
'origin': self.production_id.origin,
'date_done': fields.datetime.now(),
'lot_id': self.env['stock.move.line'].search(
[('move_id', '=', self.production_id.move_raw_ids[0].id)]).lot_id.id,
'location_id': self.production_id.move_raw_ids.filtered(lambda x: x.state not in (
'done',
'cancel')) and self.production_id.location_src_id.id or self.production_id.location_dest_id.id,
'scrap_location_id': self.env['stock.scrap']._get_default_scrap_location_id(),
'state': 'done'})]})
self.production_id.action_cancel()
if self.is_remanufacture is True:
ret = {'programming_list': [], 'is_reprogramming': self.is_reprogramming}
if self.is_reprogramming is True:
self.production_id.update_programming_state()
else:
scrap_cnc = self.production_id.workorder_ids.filtered(lambda crw: crw.routing_type == 'CNC加工').cnc_ids
scrap_cmm = self.production_id.workorder_ids.filtered(lambda cm: cm.routing_type == 'CNC加工').cmm_ids
for item_line in scrap_cnc:
vals = {
'sequence_number': item_line.sequence_number,
'program_name': item_line.program_name,
'cutting_tool_name': item_line.cutting_tool_name,
'cutting_tool_no': item_line.cutting_tool_no,
'processing_type': item_line.processing_type,
'margin_x_y': item_line.margin_x_y,
'margin_z': item_line.margin_z,
'depth_of_processing_z': item_line.depth_of_processing_z,
'cutting_tool_extension_length': item_line.cutting_tool_extension_length,
'estimated_processing_time': item_line.estimated_processing_time,
'cutting_tool_handle_type': item_line.cutting_tool_handle_type,
'ftp_path': item_line.program_path,
'processing_panel': item_line.workorder_id.processing_panel,
'program_create_date': datetime.strftime(item_line.program_create_date,
'%Y-%m-%d %H:%M:%S'),
'remark': item_line.remark
}
ret['programming_list'].append(vals)
for cmm_line in scrap_cmm:
vals = {
'sequence_number': cmm_line.sequence_number,
'program_name': cmm_line.program_name,
'ftp_path': cmm_line.program_path,
'processing_panel': item_line.workorder_id.processing_panel,
'program_create_date': datetime.strftime(
cmm_line.program_create_date,
'%Y-%m-%d %H:%M:%S')
}
ret['programming_list'].append(vals)
new_production = self.production_id.recreateManufacturing(ret)
self.production_id.write({'remanufacture_production_id': new_production.id})
if self.is_reprogramming is False:
for panel in new_production.product_id.model_processing_panel.split(','):
scrap_cnc_workorder = max(
self.production_id.workorder_ids.filtered(
lambda
scn: scn.processing_panel == panel and scn.routing_type == 'CNC加工'),
key=lambda w: w.create_date)
scrap_pre_workorder = max(self.production_id.workorder_ids.filtered(
lambda
pr: pr.processing_panel == panel and pr.routing_type == '装夹预调'),
key=lambda w1: w1.create_date)
new_cnc_workorder = new_production.workorder_ids.filtered(
lambda
nc: nc.processing_panel == panel and nc.routing_type == 'CNC加工')
new_cnc_workorder.write({'cnc_worksheet': scrap_cnc_workorder.cnc_worksheet})
new_pre_workorder = new_production.workorder_ids.filtered(lambda
p: p.routing_type == '装夹预调' and p.processing_panel == panel)
new_pre_workorder.write({'processing_drawing': scrap_pre_workorder.processing_drawing})

View File

@@ -6,28 +6,14 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<form> <form>
<sheet> <sheet>
<field name="production_id" invisible="1"/> <field name="production_id" invisible="True"/>
<field name="programming_states" invisible="1"/>
<div> <div>
重新生成制造订单 重新生成制造订单
<field name="is_remanufacture" force_save="1"/> <field name="is_remanufacture"/>
</div> </div>
<div attrs='{"invisible": [("reprogramming_num","=",0)]}'> <div>
注意: 该制造订单产品已申请重新编程次数为<field 申请重新编程
name="reprogramming_num" string="" <field name="is_reprogramming" attrs='{"invisible": [("is_remanufacture","=",False)]}'/>
readonly="1"
style='color:red;'/>,且当前编程状态为
<field name="programming_states" string=""
decoration-info="programming_states == '待编程'"
decoration-success="programming_states == '已下发'"
decoration-warning="programming_states =='编程中'"
decoration-danger="programming_states =='已编程'" readonly="1"/>
</div>
<div attrs='{"invisible": [("is_remanufacture","=",False)]}'>
<span style='font-weight:bold;'>申请重新编程
<field name="is_reprogramming" force_save="1"
attrs='{"readonly": [("programming_states","not in",["已下发"])]}'/>
</span>
</div> </div>
<footer> <footer>
<button string="确认" name="confirm" type="object" class="oe_highlight" confirm="是否确认报废"/> <button string="确认" name="confirm" type="object" class="oe_highlight" confirm="是否确认报废"/>
@@ -42,9 +28,6 @@
<field name="name">报废</field> <field name="name">报废</field>
<field name="res_model">sf.production.wizard</field> <field name="res_model">sf.production.wizard</field>
<field name="view_mode">form</field> <field name="view_mode">form</field>
<!-- <field name="context">{-->
<!-- 'default_production_id': active_id}-->
<!-- </field>-->
<field name="target">new</field> <field name="target">new</field>
</record> </record>

View File

@@ -71,9 +71,9 @@ class ReworkWizard(models.TransientModel):
lambda ap: ap.processing_panel == panel.name and ap.state != 'rework') lambda ap: ap.processing_panel == panel.name and ap.state != 'rework')
if panel_workorder: if panel_workorder:
panel_workorder.write({'state': 'rework'}) panel_workorder.write({'state': 'rework'})
# panel_workorder.filtered( panel_workorder.filtered(
# lambda wo: wo.routing_type == '装夹预调').workpiece_delivery_ids.filtered( lambda wo: wo.routing_type == '装夹预调').workpiece_delivery_ids.filtered(
# lambda wd: wd.status == '待下发').write({'status': '已取消'}) lambda wd: wd.status == '待下发').write({'status': '已取消'})
# workpiece = self.env['sf.workpiece.delivery'].search([('status', '=', '待下发'), ( # workpiece = self.env['sf.workpiece.delivery'].search([('status', '=', '待下发'), (
# 'workorder_id', '=', # 'workorder_id', '=',
# panel_workorder.filtered(lambda wd: wd.routing_type == '装夹预调').id)]) # panel_workorder.filtered(lambda wd: wd.routing_type == '装夹预调').id)])

View File

@@ -16,10 +16,10 @@
attrs='{"invisible": [("routing_type","=","装夹预调")]}' widget="many2many_tags"/> attrs='{"invisible": [("routing_type","=","装夹预调")]}' widget="many2many_tags"/>
</group> </group>
<div attrs='{"invisible": [("reprogramming_num","=",0)]}'> <div attrs='{"invisible": [("reprogramming_num","=",0)]}'>
注意: 该制造订单产品已申请重新编程次数为<field 注意: 该制造订单产品已重复编程过<field
name="reprogramming_num" string="" name="reprogramming_num" string=""
readonly="1" readonly="1"
style='color:red;'/>,且当前编程状态为 style='color:red;'/>,且当前编程状态为
<field name="programming_state" string="" <field name="programming_state" string=""
decoration-info="programming_state == '待编程'" decoration-info="programming_state == '待编程'"
decoration-success="programming_state == '已下发'" decoration-success="programming_state == '已下发'"

View File

@@ -4,22 +4,30 @@
<field name="name">sf.workpiece.delivery.wizard.form.view</field> <field name="name">sf.workpiece.delivery.wizard.form.view</field>
<field name="model">sf.workpiece.delivery.wizard</field> <field name="model">sf.workpiece.delivery.wizard</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<form js_class="remove_focus_view"> <form>
<sheet> <sheet>
<field name="delivery_ids" invisible="True"/> <field name="delivery_ids" invisible="True"/>
<field name="workorder_ids" invisible="True"/> <field name="workorder_id" invisible="True"/>
<field name="delivery_type" invisible="True"/> <field name="type" invisible="True"/>
<field name="confirm_button" invisible="1"/> <group attrs="{'invisible': [('type', 'in', ['运送空料架'])]}">
<field name="_barcode_scanned" widget="barcode_handler"/>
<group col="1">
<field name="production_ids" readonly="1" widget="many2many_tags" string="制造订单号"/> <field name="production_ids" readonly="1" widget="many2many_tags" string="制造订单号"/>
<field name="delivery_type" readonly="1"/> <div class="o_address_format">
<field name="feeder_station_start_id" options="{'no_create': True}" required="1"/> <lable for="rfid_code"></lable>
<field name="workcenter_id" options="{'no_create': True}"/> <field name="rfid_code" class="o_address_zip"/>
<button name="recognize_production" string="识别" type="object" class="oe_highlight"/>
</div>
<field name="destination_production_line_id" readonly="1"/>
<field name="route_id"/>
</group> </group>
<group attrs="{'invisible': [('type', 'in', ['运送空料架'])]}">
<field name="feeder_station_start_id" focesave="1" readonly="1"/>
<field name="feeder_station_destination_id" focesave="1" readonly="1"/>
</group>
<div attrs="{'invisible': [('type', 'in', ['上产线','下产线'])]}">
是否确定配送
</div>
<footer> <footer>
<button string="确认配送" name="dispatch_confirm" type="object" class="oe_highlight o_wizard_confirm_button" attrs="{'invisible': [('confirm_button', '!=', '确认配送')]}"/> <button string="配送" name="confirm" type="object" class="oe_highlight"/>
<button string="确认解除" name="dispatch_confirm" type="object" class="oe_highlight o_wizard_confirm_button" attrs="{'invisible': [('confirm_button', '!=', '确认解除')]}"/>
<button string="取消" class="btn btn-secondary" special="cancel"/> <button string="取消" class="btn btn-secondary" special="cancel"/>
</footer> </footer>
</sheet> </sheet>

View File

@@ -1,208 +1,88 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Part of YiZuo. See LICENSE file for full copyright and licensing details. # Part of YiZuo. See LICENSE file for full copyright and licensing details.
import json
import logging import logging
from odoo.exceptions import UserError, ValidationError from odoo.exceptions import UserError, ValidationError
from datetime import datetime, date from datetime import datetime
from odoo import models, api, fields from odoo import models, api, fields, _
def convert_datetime(obj):
if isinstance(obj, (datetime, date)):
return obj.isoformat() # 将 datetime 或 date 对象转换为 ISO 8601 字符串格式
raise TypeError(f"Type {type(obj)} not serializable")
class WorkpieceDeliveryWizard(models.TransientModel): class WorkpieceDeliveryWizard(models.TransientModel):
_name = 'sf.workpiece.delivery.wizard' _name = 'sf.workpiece.delivery.wizard'
_inherit = ["barcodes.barcode_events_mixin"]
_description = '工件配送' _description = '工件配送'
delivery_ids = fields.Many2many('sf.workpiece.delivery', string='配送')
rfid_code = fields.Char('rfid码') rfid_code = fields.Char('rfid码')
delivery_ids = fields.Many2many('sf.workpiece.delivery', string='配送') workorder_id = fields.Many2one('mrp.workorder', string='')
workorder_ids = fields.Many2many('mrp.workorder', string='工单')
production_ids = fields.Many2many('mrp.production', string='制造订单号') production_ids = fields.Many2many('mrp.production', string='制造订单号')
destination_production_line_id = fields.Many2one('sf.production.line', '目的生产线') destination_production_line_id = fields.Many2one('sf.production.line', '目的生产线')
route_id = fields.Many2one('sf.agv.task.route', '任务路线', domain=[('route_type', 'in', ['上产线', '下产线'])]) route_id = fields.Many2one('sf.agv.task.route', '任务路线', domain=[('route_type', 'in', ['上产线', '下产线'])])
feeder_station_start_id = fields.Many2one('sf.agv.site', '起点接驳站') feeder_station_start_id = fields.Many2one('sf.agv.site', '起点接驳站')
feeder_station_destination_id = fields.Many2one('sf.agv.site', '目的接驳站') feeder_station_destination_id = fields.Many2one('sf.agv.site', '目的接驳站')
workcenter_id = fields.Many2one(string='所属区域', comodel_name='mrp.workcenter', tracking=True) type = fields.Selection(
confirm_button = fields.Char('按钮名称') [('上产线', '上产线'), ('下产线', '下产线'), ('运送空料架', '运送空料架')], string='类型')
@api.onchange('delivery_type')
def _onchange_type(self):
if self.delivery_type:
routes = self.env['sf.agv.task.route'].search([('route_type', '=', self.delivery_type)])
if self.workcenter_id:
routes = routes.filtered(lambda a: a.start_site_id.workcenter_id.id == self.workcenter_id.id)
start_site_ids = routes.mapped('start_site_id.id')
workcenter_ids = routes.mapped('end_site_id.workcenter_id.id')
if workcenter_ids:
self.workcenter_id = workcenter_ids[0]
return {
'domain':
{
'feeder_station_start_id': [('id', 'in', start_site_ids)],
'workcenter_id': [('id', 'in', workcenter_ids)],
}
}
else:
return {
'domain':
{
'feeder_station_start_id': [],
'workcenter_id': [],
}
}
def _get_agv_route_type_selection(self):
return self.env['sf.agv.task.route'].fields_get(['route_type'])['route_type']['selection']
delivery_type = fields.Selection(selection=_get_agv_route_type_selection, string='类型')
def dispatch_confirm(self):
if len(self.workorder_ids) < 4:
return {
'type': 'ir.actions.client',
'tag': 'dispatch_confirm',
'params': {
'workorder_count': len(self.workorder_ids),
'active_id': self.id,
'context': self.env.context
}
}
else:
scheduling = self.confirm()
# 显示通知
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'target': 'new',
'params': {
'message': '任务下发成功AGV任务调度编号为【%s' % scheduling['name'],
'type': 'success',
'sticky': False,
'next': {'type': 'ir.actions.act_window_close'},
}
}
def confirm(self): def confirm(self):
try: if self.type != '运送空料架':
# if self.workorder_id: if not self.route_id:
# self.workorder_id.workpiece_delivery_ids[0].agv_scheduling_id() raise UserError('请选择路线')
# else: if self.workorder_id:
# is_not_production_line = 0 self.workorder_id.workpiece_delivery_ids[0]._delivery_avg()
# same_production_line_id = None else:
# notsame_production_line_arr = [] is_not_production_line = 0
# for item in self.production_ids: same_production_line_id = None
# if same_production_line_id is None: notsame_production_line_arr = []
# same_production_line_id = item.production_line_id.id for item in self.production_ids:
# if item.production_line_id.id != same_production_line_id: if same_production_line_id is None:
# notsame_production_line_arr.append(item.name) same_production_line_id = item.production_line_id.id
# notsame_production_line_str = ','.join(map(str, notsame_production_line_arr)) if item.production_line_id.id != same_production_line_id:
# if is_not_production_line >= 1: notsame_production_line_arr.append(item.name)
# raise UserError('制造订单号为%s的目的生产线不一致' % notsame_production_line_str) notsame_production_line_str = ','.join(map(str, notsame_production_line_arr))
# else: if is_not_production_line >= 1:
scheduling = self.env['sf.agv.scheduling'].add_scheduling( raise UserError('制造订单号为%s的目的生产线不一致' % notsame_production_line_str)
agv_start_site_name=self.feeder_station_start_id.name, else:
agv_route_type=self.delivery_type, self.delivery_ids._delivery_avg()
workorders=self.workorder_ids,
)
# 如果关联了工件配送单,则修改状态为已下发
if self.delivery_ids:
val = {
'status': '已下发',
'agv_scheduling_id': scheduling.id,
'feeder_station_start_id': scheduling.start_site_id.id,
}
# 如果agv任务已经下发则修改工件配送单信息
if scheduling.state == '配送中':
val.update({
'feeder_station_destination_id': scheduling.end_site_id.id,
'route_id': scheduling.agv_route_id.id,
'task_delivery_time': fields.Datetime.now()
})
self.delivery_ids.write(val)
# 如果是解除装夹工单,则需要处理工单逻辑 def recognize_production(self):
for item in self.workorder_ids: # production_ids = []
if item.routing_type == '解除装夹' and item.state == 'ready': # delivery_ids = []
item.button_start() # aa = self.production_ids.workorder_ids.filtered(
item.button_finish() # lambda b: b.routing_type == "装夹预调").workpiece_delivery_ids.filtered(
# lambda c: c.rfid_code == self.rfid_code)
return scheduling.read()[0] # logging.info('aa:%s' % aa)
except Exception as e: if len(self.production_ids) == 4:
logging.info('%s任务下发失败:%s' % (self.delivery_type, e)) raise UserError('只能配送四个制造订单')
raise UserError('%s任务下发失败:%s' % (self.delivery_type, e)) else:
if self.rfid_code:
# def recognize_production(self): wd = self.env['sf.workpiece.delivery'].search(
# # production_ids = [] [('type', '=', self.delivery_ids[0].type), ('rfid_code', '=', self.rfid_code),
# # delivery_ids = [] ('status', '=', self.delivery_ids[0].status)])
# # aa = self.production_ids.workorder_ids.filtered( if wd:
# # lambda b: b.routing_type == "装夹预调").workpiece_delivery_ids.filtered( if wd.production_line_id.id == self.delivery_ids[0].production_line_id.id:
# # lambda c: c.rfid_code == self.rfid_code) # production_ids.append(wd.production_id)
# # logging.info('aa:%s' % aa) # delivery_ids.append(wd.id)
# if len(self.production_ids) == 4: # 将对象添加到对应的同模型且是多对多类型里
# raise UserError('只能配送四个制造订单') self.production_ids |= wd.production_id
# else: self.delivery_ids |= wd
# if self.rfid_code: self.rfid_code = False
# wd = self.env['sf.workpiece.delivery'].search( # self.production_ids = [(6, 0, production_ids)]
# [('type', '=', self.delivery_ids[0].type), ('rfid_code', '=', self.rfid_code), # self.delivery_ids = [(6, 0, delivery_ids)]
# ('status', '=', self.delivery_ids[0].status)]) else:
# if wd: raise UserError('该rfid对应的制造订单号为%s的目的生产线不一致' % wd.production_id.name)
# if wd.production_line_id.id == self.delivery_ids[0].production_line_id.id: return {
# # production_ids.append(wd.production_id) 'name': _('确认'),
# # delivery_ids.append(wd.id) 'type': 'ir.actions.act_window',
# # 将对象添加到对应的同模型且是多对多类型里 'view_mode': 'form',
# self.production_ids |= wd.production_id 'res_model': 'sf.workpiece.delivery.wizard',
# self.delivery_ids |= wd 'target': 'new',
# self.rfid_code = False 'context': {
# # self.production_ids = [(6, 0, production_ids)] 'default_delivery_ids': [(6, 0, self.delivery_ids.ids)],
# # self.delivery_ids = [(6, 0, delivery_ids)] 'default_production_ids': [(6, 0, self.production_ids.ids)],
# else: 'default_route_id': self.delivery_ids[0].route_id.id,
# raise UserError('该rfid对应的制造订单号为%s的目的生产线不一致' % wd.production_id.name) 'default_type': self.delivery_ids[0].type
# return { }}
# 'name': _('确认'),
# 'type': 'ir.actions.act_window',
# 'view_mode': 'form',
# 'res_model': 'sf.workpiece.delivery.wizard',
# 'target': 'new',
# 'context': {
# 'default_delivery_ids': [(6, 0, self.delivery_ids.ids)],
# 'default_production_ids': [(6, 0, self.production_ids.ids)],
# 'default_route_id': self.delivery_ids[0].route_id.id,
# 'default_type': self.delivery_ids[0].type
# }}
@api.onchange('route_id') @api.onchange('route_id')
def onchange_route(self): def onchange_route(self):
if self.route_id: if self.route_id:
self.feeder_station_start_id = self.route_id.start_site_id.id self.feeder_station_start_id = self.route_id.start_site_id.id
self.feeder_station_destination_id = self.route_id.end_site_id.id self.feeder_station_destination_id = self.route_id.end_site_id.id
def on_barcode_scanned(self, barcode):
delivery_type = self.env.context.get('default_delivery_type')
if delivery_type == '上产线':
workorder = self.env['mrp.workorder'].search(
[('production_line_state', '=', '待上产线'), ('rfid_code', '=', barcode),
('state', '=', 'done')])
# 找到对应的配送单
delivery = self.env['sf.workpiece.delivery'].search(
[('type', '=', '上产线'), ('rfid_code', '=', barcode),
('status', '=', '待下发')])
if delivery:
self.delivery_ids |= delivery
elif delivery_type == '运送空料架':
workorder = self.env['mrp.workorder'].search(
[('routing_type', '=', '解除装夹'), ('rfid_code', '=', barcode),
('state', '=', 'ready')])
if workorder:
if (len(self.production_ids) > 0 and
workorder.production_line_id.id != self.production_ids[0].production_line_id.id):
raise UserError('该rfid对应的制造订单号为%s的目的生产线不一致' % workorder.production_id.name)
# 将对象添加到对应的同模型且是多对多类型里
self.production_ids |= workorder.production_id
self.workorder_ids |= workorder
else:
raise UserError('该rfid码对应的工单不存在')
return

File diff suppressed because it is too large Load Diff

View File

@@ -24,13 +24,11 @@ class Sf_Mrs_Connect(http.Controller):
ret = json.loads(datas) ret = json.loads(datas)
ret = json.loads(ret['result']) ret = json.loads(ret['result'])
logging.info('下发编程单:%s' % ret) logging.info('下发编程单:%s' % ret)
domain = [('programming_no', '=', ret['programming_no'])]
if ret['manufacturing_type'] == 'scrap':
domain += [('state', 'not in', ['done', 'scrap', 'cancel'])]
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(
[('programming_no', '=', ret['programming_no'])])
if productions: if productions:
# 拉取所有加工面的程序文件 # # 拉取所有加工面的程序文件
for r in ret['processing_panel'].split(','): for r in ret['processing_panel'].split(','):
program_path_tmp_r = os.path.join('/tmp', ret['folder_name'], 'return', r) program_path_tmp_r = os.path.join('/tmp', ret['folder_name'], 'return', r)
if os.path.exists(program_path_tmp_r): if os.path.exists(program_path_tmp_r):
@@ -50,28 +48,45 @@ class Sf_Mrs_Connect(http.Controller):
if not production.workorder_ids: if not production.workorder_ids:
production.product_id.model_processing_panel = ret['processing_panel'] production.product_id.model_processing_panel = ret['processing_panel']
production._create_workorder(ret) production._create_workorder(ret)
productions.process_range_time() # else:
else: # for panel in ret['processing_panel'].split(','):
for panel in ret['processing_panel'].split(','): # # 查询状态为进行中且工序类型为CNC加工的工单
# 查询状态为进行中且工序类型为CNC加工的工单 # cnc_workorder = production.workorder_ids.filtered(
cnc_workorder_has = production.workorder_ids.filtered( # lambda ac: ac.routing_type == 'CNC加工' and ac.state not in ['progress', 'done',
lambda ach: ach.routing_type == 'CNC加工' and ach.state not in ['progress', 'done', # 'cancel'] and ac.processing_panel == panel)
'rework', # if cnc_workorder:
'cancel'] and ach.processing_panel == panel) # if cnc_workorder.cnc_ids:
if cnc_workorder_has: # cnc_workorder.cmm_ids.sudo().unlink()
if cnc_workorder_has.cnc_ids: # cnc_workorder.cnc_ids.sudo().unlink()
cnc_workorder_has.cmm_ids.sudo().unlink() # request.env['sf.cam.work.order.program.knife.plan'].sudo().unlink_cam_plan(
cnc_workorder_has.cnc_ids.sudo().unlink() # production)
request.env['sf.cam.work.order.program.knife.plan'].sudo().unlink_cam_plan( # # program_path_tmp_panel = os.path.join('C://Users//43484//Desktop//fsdownload//test',
production) # # panel)
cnc_workorder_has.write( # program_path_tmp_panel = os.path.join('/tmp', ret['folder_name'], 'return', panel)
{'cnc_ids': cnc_workorder_has.cnc_ids.sudo()._json_cnc_processing(panel, ret), # logging.info('program_path_tmp_panel:%s' % program_path_tmp_panel)
'cmm_ids': cnc_workorder_has.cmm_ids.sudo()._json_cmm_program(panel, ret)}) # files_panel = os.listdir(program_path_tmp_panel)
# if files_panel:
# for file in files_panel:
# file_extension = os.path.splitext(file)[1]
# logging.info('file_extension:%s' % file_extension)
# if file_extension.lower() == '.pdf':
# panel_file_path = os.path.join(program_path_tmp_panel, file)
# logging.info('panel_file_path:%s' % panel_file_path)
# cnc_workorder.write(
# {'cnc_ids': cnc_workorder.cnc_ids.sudo()._json_cnc_processing(panel, ret),
# 'cmm_ids': cnc_workorder.cmm_ids.sudo()._json_cmm_program(panel, ret),
# 'cnc_worksheet': base64.b64encode(open(panel_file_path, 'rb').read())})
# pre_workorder = production.workorder_ids.filtered(
# lambda ap: ap.routing_type == '装夹预调' and ap.state not in ['done',
# 'cancel'] and ap.processing_panel == panel)
# if pre_workorder:
# pre_workorder.write(
# {'processing_drawing': base64.b64encode(open(panel_file_path, 'rb').read())})
for panel in ret['processing_panel'].split(','): for panel in ret['processing_panel'].split(','):
# 查询状态为进行中且工序类型为CNC加工的工单 # 查询状态为进行中且工序类型为CNC加工的工单
cnc_workorder = productions.workorder_ids.filtered( cnc_workorder = productions.workorder_ids.filtered(
lambda ac: ac.routing_type == 'CNC加工' and ac.state not in ['progress', 'done', 'rework' lambda ac: ac.routing_type == 'CNC加工' and ac.state not in ['progress', 'done',
'cancel'] and ac.processing_panel == panel) 'cancel'] and ac.processing_panel == panel)
if cnc_workorder: if cnc_workorder:
# program_path_tmp_panel = os.path.join('C://Users//43484//Desktop//fsdownload//test', # program_path_tmp_panel = os.path.join('C://Users//43484//Desktop//fsdownload//test',
# panel) # panel)
@@ -87,12 +102,18 @@ class Sf_Mrs_Connect(http.Controller):
logging.info('panel_file_path:%s' % panel_file_path) logging.info('panel_file_path:%s' % panel_file_path)
cnc_workorder.write({'cnc_worksheet': base64.b64encode(open(panel_file_path, 'rb').read())}) cnc_workorder.write({'cnc_worksheet': base64.b64encode(open(panel_file_path, 'rb').read())})
pre_workorder = productions.workorder_ids.filtered( pre_workorder = productions.workorder_ids.filtered(
lambda ap: ap.routing_type == '装夹预调' and ap.state not in ['done', 'rework' lambda ap: ap.routing_type == '装夹预调' and ap.state not in ['done',
'cancel'] and ap.processing_panel == panel) 'cancel'] and ap.processing_panel == panel)
if pre_workorder: if pre_workorder:
pre_workorder.write( pre_workorder.write(
{'processing_drawing': base64.b64encode(open(panel_file_path, 'rb').read())}) {'processing_drawing': base64.b64encode(open(panel_file_path, 'rb').read())})
productions.write({'programming_state': '已编程', 'work_state': '已编程'}) productions.write({'programming_state': '已编程', 'work_state': '已编程'})
cnc_program_ids = [item.id for item in productions]
workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search(
[('production_id', 'in', cnc_program_ids)])
if workpiece_delivery:
workpiece_delivery.write(
{'is_cnc_program_down': True, 'production_line_id': productions.production_line_id.id})
return json.JSONEncoder().encode(res) return json.JSONEncoder().encode(res)
else: else:
res = {'status': 0, 'message': '该制造订单暂未开始'} res = {'status': 0, 'message': '该制造订单暂未开始'}

View File

@@ -329,7 +329,6 @@ class sfProductionProcess(models.Model):
production_process.processing_day = item['processing_day'] production_process.processing_day = item['processing_day']
production_process.travel_day = item['travel_day'] production_process.travel_day = item['travel_day']
production_process.active = item['active'] production_process.active = item['active']
production_process.sequence = item['sequence']
else: else:
self.create({ self.create({
"name": item['name'], "name": item['name'],
@@ -339,7 +338,6 @@ class sfProductionProcess(models.Model):
"processing_day": item['processing_day'], "processing_day": item['processing_day'],
"travel_day": item['travel_day'], "travel_day": item['travel_day'],
"active": item['active'], "active": item['active'],
"sequence": item['sequence']
}) })
else: else:
raise ValidationError("表面工艺认证未通过") raise ValidationError("表面工艺认证未通过")
@@ -367,7 +365,6 @@ class sfProductionProcess(models.Model):
"processing_day": item['processing_day'], "processing_day": item['processing_day'],
"travel_day": item['travel_day'], "travel_day": item['travel_day'],
"active": item['active'], "active": item['active'],
"sequence": item['sequence']
}) })
else: else:
production_process.name = item['name'] production_process.name = item['name']
@@ -376,7 +373,6 @@ class sfProductionProcess(models.Model):
production_process.processing_day = item['processing_day'] production_process.processing_day = item['processing_day']
production_process.travel_day = item['travel_day'] production_process.travel_day = item['travel_day']
production_process.active = item['active'] production_process.active = item['active']
production_process.sequence = item['sequence']
else: else:
raise ValidationError("表面工艺认证未通过") raise ValidationError("表面工艺认证未通过")
@@ -1092,7 +1088,6 @@ class sfProductionProcessParameter(models.Model):
production_process_parameter.process_id = process.id production_process_parameter.process_id = process.id
production_process_parameter.materials_model_ids = self.env['sf.materials.model'].search( production_process_parameter.materials_model_ids = self.env['sf.materials.model'].search(
[('materials_no', 'in', item['materials_model_ids_codes'])]) [('materials_no', 'in', item['materials_model_ids_codes'])])
production_process_parameter.processing_mm = item['processing_mm']
else: else:
self.create({ self.create({
"name": item['name'], "name": item['name'],
@@ -1104,7 +1099,6 @@ class sfProductionProcessParameter(models.Model):
"process_id": process.id, "process_id": process.id,
"materials_model_ids": self.env['sf.materials.model'].search( "materials_model_ids": self.env['sf.materials.model'].search(
[('materials_no', 'in', item['materials_model_ids_codes'])]), [('materials_no', 'in', item['materials_model_ids_codes'])]),
"processing_mm": item['processing_mm']
}) })
else: else:
raise ValidationError("表面工艺可选参数认证未通过") # 定时同步表面工艺 raise ValidationError("表面工艺可选参数认证未通过") # 定时同步表面工艺
@@ -1135,7 +1129,6 @@ class sfProductionProcessParameter(models.Model):
"process_id": process.id, "process_id": process.id,
'materials_model_ids': self.env['sf.materials.model'].search( 'materials_model_ids': self.env['sf.materials.model'].search(
[('materials_no', 'in', item['materials_model_ids_codes'])]), [('materials_no', 'in', item['materials_model_ids_codes'])]),
'processing_mm': item['processing_mm']
}) })
else: else:
production_process_parameter.name = item['name'] production_process_parameter.name = item['name']
@@ -1146,7 +1139,6 @@ class sfProductionProcessParameter(models.Model):
production_process_parameter.materials_model_ids = self.env['sf.materials.model'].search( production_process_parameter.materials_model_ids = self.env['sf.materials.model'].search(
[('materials_no', 'in', item['materials_model_ids_codes'])]) [('materials_no', 'in', item['materials_model_ids_codes'])])
production_process_parameter.active = item['active'] production_process_parameter.active = item['active']
production_process_parameter.processing_mm = item['processing_mm']
else: else:
raise ValidationError("表面工艺可选参数认证未通过") raise ValidationError("表面工艺可选参数认证未通过")

View File

@@ -76,7 +76,7 @@
</div> </div>
<div> <div>
<h2>AGV参数配置</h2> <h2>AGV参数配置</h2>
<div class="row mt16 o_settings_container" id="agv_config"> <div class="row mt16 o_settings_container">
<div class="col-12 col-lg-6 o_setting_box"> <div class="col-12 col-lg-6 o_setting_box">
<div class="o_setting_left_pane"/> <div class="o_setting_left_pane"/>
<div class="o_setting_right_pane"> <div class="o_setting_right_pane">

View File

@@ -337,7 +337,6 @@
name="空料架配送" name="空料架配送"
sequence="11" sequence="11"
action="sf_manufacturing.sf_workpiece_delivery_empty_racks_act" action="sf_manufacturing.sf_workpiece_delivery_empty_racks_act"
groups="base.group_system"
parent="mrp.menu_mrp_manufacturing" parent="mrp.menu_mrp_manufacturing"
/> />
<!-- <menuitem --> <!-- <menuitem -->

View File

@@ -16,8 +16,6 @@ class Action_Plan_All_Wizard(models.TransientModel):
# 选择生产线 # 选择生产线
production_line_id = fields.Many2one('sf.production.line', string=u'生产线', required=True) production_line_id = fields.Many2one('sf.production.line', string=u'生产线', required=True)
date_planned_start = fields.Datetime(string='计划开始时间', index=True, copy=False,
default=fields.Datetime.now)
# 接收传递过来的计划ID # 接收传递过来的计划ID
plan_ids = fields.Many2many('sf.production.plan', string=u'计划ID') plan_ids = fields.Many2many('sf.production.plan', string=u'计划ID')
@@ -35,7 +33,6 @@ class Action_Plan_All_Wizard(models.TransientModel):
# 拿到计划对象 # 拿到计划对象
plan_obj = self.env['sf.production.plan'].browse(plan.id) plan_obj = self.env['sf.production.plan'].browse(plan.id)
plan_obj.production_line_id = self.production_line_id.id plan_obj.production_line_id = self.production_line_id.id
plan.date_planned_start = self.date_planned_start
plan_obj.do_production_schedule() plan_obj.do_production_schedule()
# plan_obj.state = 'done' # plan_obj.state = 'done'
print('处理计划:', plan.id, '完成') print('处理计划:', plan.id, '完成')

View File

@@ -7,7 +7,6 @@
<form> <form>
<group> <group>
<field name="production_line_id" domain="[('name', 'ilike', 'CNC')]"/> <field name="production_line_id" domain="[('name', 'ilike', 'CNC')]"/>
<field name="date_planned_start"/>
</group> </group>
<footer> <footer>
<button string="确认排程" name="action_plan_all" type="object" class="btn-primary"/> <button string="确认排程" name="action_plan_all" type="object" class="btn-primary"/>

View File

@@ -48565,16 +48565,3 @@ msgstr ""
#: model:ir.model.fields.selection,name:sf_maintenance.selection__maintenance_equipment__heightened_way__chilunjia #: model:ir.model.fields.selection,name:sf_maintenance.selection__maintenance_equipment__heightened_way__chilunjia
msgid "齿轮架驱动" msgid "齿轮架驱动"
msgstr "" msgstr ""
#. module: sf_manufacturing
#. odoo-python
#: code:addons/sf_manufacturing/models/mrp_production.py:0
#: model:ir.actions.act_window,name:sf_manufacturing.action_sf_production_wizard
#: model:ir.model.fields.selection,name:sf_manufacturing.selection__mrp_production__state__scrap
#: model:ir.model.fields.selection,name:sf_manufacturing.selection__mrp_workorder__test_results__报废
#: model:ir.model.fields.selection,name:sf_manufacturing.selection__sf_detection_result__test_results__报废
#: model_terms:ir.ui.view,arch_db:sf_manufacturing.custom_mrp_production_form_view
#: model_terms:ir.ui.view,arch_db:sf_manufacturing.custom_view_mrp_production_filter
#, python-format
msgid "报废"
msgstr "报废"

View File

@@ -20,8 +20,7 @@
'views/sale_order_view.xml', 'views/sale_order_view.xml',
'views/res_partner_view.xml', 'views/res_partner_view.xml',
'views/purchase_order_view.xml', 'views/purchase_order_view.xml',
'views/quick_easy_order_view.xml', 'views/quick_easy_order_view.xml'
'views/purchase_menu.xml'
], ],
'assets': { 'assets': {
'web.assets_backend': [ 'web.assets_backend': [

View File

@@ -371,7 +371,6 @@ class QuickEasyOrder(models.Model):
product_bom_purchase.bom_create_line_has(purchase_embryo) product_bom_purchase.bom_create_line_has(purchase_embryo)
order_id.with_user(self.env.ref("base.user_admin")).sale_order_create_line(product, item) order_id.with_user(self.env.ref("base.user_admin")).sale_order_create_line(product, item)
except Exception as e: except Exception as e:
logging.error('工厂创建销售订单和产品失败,请联系管理员'.format(e))
# self.cr.rollback() # self.cr.rollback()
return UserError('工厂创建销售订单和产品失败,请联系管理员') return UserError('工厂创建销售订单和产品失败,请联系管理员')

View File

@@ -13,11 +13,6 @@ READONLY_FIELD_STATES = {
class ReSaleOrder(models.Model): class ReSaleOrder(models.Model):
_inherit = 'sale.order' _inherit = 'sale.order'
mrp_production_count = fields.Integer(
"Count of MO generated",
compute='_compute_mrp_production_ids',
groups='mrp.group_mrp_user,sf_base.group_sale_salemanager,sf_base.group_sale_director')
logistics_way = fields.Selection([('自提', '自提'), ('到付', '到付'), ('在线支付', '在线支付')], string='物流方式') logistics_way = fields.Selection([('自提', '自提'), ('到付', '到付'), ('在线支付', '在线支付')], string='物流方式')
state = fields.Selection( state = fields.Selection(
selection=[ selection=[
@@ -60,7 +55,7 @@ class ReSaleOrder(models.Model):
deadline_of_delivery, payments_way, pay_way): deadline_of_delivery, payments_way, pay_way):
now_time = datetime.datetime.now() now_time = datetime.datetime.now()
partner = self.get_customer() partner = self.get_customer()
data = { order_id = self.env['sale.order'].sudo().create({
'company_id': company_id.id, 'company_id': company_id.id,
'date_order': now_time, 'date_order': now_time,
'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),
@@ -71,18 +66,10 @@ class ReSaleOrder(models.Model):
'person_of_delivery': delivery_name, 'person_of_delivery': delivery_name,
'telephone_of_delivery': delivery_telephone, 'telephone_of_delivery': delivery_telephone,
'address_of_delivery': delivery_address, 'address_of_delivery': delivery_address,
'deadline_of_delivery': deadline_of_delivery,
'payments_way': payments_way, 'payments_way': payments_way,
'pay_way': pay_way, 'pay_way': pay_way,
} })
if deadline_of_delivery:
# deadline_of_delivery字段存在为false字符串情况
if not isinstance(deadline_of_delivery, str):
data.update({'deadline_of_delivery': deadline_of_delivery})
else:
if deadline_of_delivery != "False":
data.update({'deadline_of_delivery': deadline_of_delivery})
order_id = self.env['sale.order'].sudo().create(data)
return order_id return order_id
def write(self, vals): def write(self, vals):
@@ -224,44 +211,6 @@ class RePurchaseOrder(models.Model):
if not line.taxes_id: if not line.taxes_id:
raise UserError('请对【产品】中的【税】进行选择') raise UserError('请对【产品】中的【税】进行选择')
def get_purchase_order(self, consecutive_process_parameters, production, product_id_to_production_names):
server_product_process = []
production_process = product_id_to_production_names.get(
production.product_id.id)
for pp in consecutive_process_parameters:
if pp.gain_way == '外协':
server_template = self.env['product.template'].search(
[('server_product_process_parameters_id', '=', pp.id),
('detailed_type', '=', 'service')])
purchase_order_line = self.env['purchase.order.line'].search(
[('product_id', '=', server_template.product_variant_id.id),
('product_qty', '=', len(production_process))], limit=1, order='id desc')
if not purchase_order_line:
server_product_process.append((0, 0, {
'product_id': server_template.product_variant_id.id,
'product_qty': len(production_process),
'product_uom': server_template.uom_id.id
}))
else:
for item in purchase_order_line:
if production.name in production_process:
purchase_order = self.env['purchase.order'].search(
[('state', '=', 'draft'), ('origin', '=', ','.join(production_process)),
('id', '=', item.order_id.id)])
if not purchase_order:
server_product_process.append((0, 0, {
'product_id': server_template.product_variant_id.id,
'product_qty': len(production_process),
'product_uom': server_template.uom_id.id
}))
if server_product_process:
self.env['purchase.order'].sudo().create({
'partner_id': server_template.seller_ids.partner_id.id,
'origin': ','.join(production_process),
'state': 'draft',
'order_line': server_product_process})
# self.env.cr.commit()
@api.onchange('order_line') @api.onchange('order_line')
def _onchange_order_line(self): def _onchange_order_line(self):
for order in self: for order in self:
@@ -281,14 +230,6 @@ class RePurchaseOrder(models.Model):
if picking_id.move_ids: if picking_id.move_ids:
for move_id in picking_id.move_ids: for move_id in picking_id.move_ids:
move_id.put_move_line() move_id.put_move_line()
for line in item.order_line:
if line.product_id.categ_type == '表面工艺':
for production_name in item.origin.split(','):
production = self.env['mrp.production'].search([('name', '=', production_name)])
for workorder in production.workorder_ids.filtered(
lambda wd: wd.routing_type == '表面工艺' and wd.state == 'waiting' and line.product_id.server_product_process_parameters_id == wd.surface_technics_parameters_id):
workorder.state = 'ready'
return result return result

View File

@@ -96,28 +96,5 @@ access_product_supplierinfo_group_plan_director,product.supplierinfo user,produc
access_product_category_group_plan_director,product.category user,product.model_product_category,sf_base.group_plan_director,1,1,1,0 access_product_category_group_plan_director,product.category user,product.model_product_category,sf_base.group_plan_director,1,1,1,0
access_purchase_report_sf_base_group_purchase,purchase_report_sf_base_group_purchase,purchase.model_purchase_report,sf_base.group_purchase,1,0,0,0
access_purchase_report_sf_base_group_purchase_director,purchase_report_sf_base_group_purchase_director,purchase.model_purchase_report,sf_base.group_purchase_director,1,0,0,0
access_sale_order_sf_base_group_purchase,sale_order_sf_base_group_purchase,model_sale_order,sf_base.group_purchase,1,0,0,0
access_sale_order_sf_base_group_purchase_director,sale_order_sf_base_group_purchase_director,model_sale_order,sf_base.group_purchase_director,1,0,0,0
access_quality_check_group_sale_salemanager,quality_check_group_sale_salemanager,quality.model_quality_check,sf_base.group_sale_salemanager,1,0,0,0
access_quality_check_group_sale_director,quality_check_group_sale_director,quality.model_quality_check,sf_base.group_sale_director,1,0,0,0
access_stock_picking_group_sale_salemanager,stock_picking_group_sale_salemanager,stock.model_stock_picking,sf_base.group_sale_salemanager,1,0,0,0
access_stock_picking_group_sale_director,stock_picking_group_sale_director,stock.model_stock_picking,sf_base.group_sale_director,1,0,0,0
access_mrp_workorder_group_sale_salemanager,mrp_workorder_group_sale_salemanager,mrp.model_mrp_workorder,sf_base.group_sale_salemanager,1,0,0,0
access_mrp_workorder_group_sale_director,mrp_workorder_group_sale_director,mrp.model_mrp_workorder,sf_base.group_sale_director,1,0,0,0
access_mrp_unbuild_group_sale_salemanager,mrp_unbuild_group_sale_salemanager,mrp.model_mrp_unbuild,sf_base.group_sale_salemanager,1,0,0,0
access_mrp_unbuild_group_sale_director,mrp_unbuild_group_sale_director,mrp.model_mrp_unbuild,sf_base.group_sale_director,1,0,0,0
access_mrp_workcenter_productivity_group_sale_salemanager,mrp_workcenter_productivity_group_sale_salemanager,mrp.model_mrp_workcenter_productivity,sf_base.group_sale_salemanager,1,0,0,0
access_mrp_workcenter_productivity_group_sale_director,mrp_workcenter_productivity_group_sale_director,mrp.model_mrp_workcenter_productivity,sf_base.group_sale_director,1,0,0,0
access_sf_detection_result_group_sale_salemanager,sf_detection_result_group_sale_salemanager,sf_manufacturing.model_sf_detection_result,sf_base.group_sale_salemanager,1,0,0,0
access_sf_detection_result_group_sale_director,sf_detection_result_group_sale_director,sf_manufacturing.model_sf_detection_result,sf_base.group_sale_director,1,0,0,0
access_stock_scrap_group_sale_salemanager,stock_scrap_group_sale_salemanager,stock.model_stock_scrap,sf_base.group_sale_salemanager,1,0,0,0
access_stock_scrap_group_sale_director,stock_scrap_group_sale_director,stock.model_stock_scrap,sf_base.group_sale_director,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
96
97
98
access_quality_check_group_sale_salemanager quality_check_group_sale_salemanager quality.model_quality_check sf_base.group_sale_salemanager 1 0 0 0
access_quality_check_group_sale_director quality_check_group_sale_director quality.model_quality_check sf_base.group_sale_director 1 0 0 0
access_stock_picking_group_sale_salemanager stock_picking_group_sale_salemanager stock.model_stock_picking sf_base.group_sale_salemanager 1 0 0 0
access_stock_picking_group_sale_director stock_picking_group_sale_director stock.model_stock_picking sf_base.group_sale_director 1 0 0 0
access_mrp_workorder_group_sale_salemanager mrp_workorder_group_sale_salemanager mrp.model_mrp_workorder sf_base.group_sale_salemanager 1 0 0 0
access_mrp_workorder_group_sale_director mrp_workorder_group_sale_director mrp.model_mrp_workorder sf_base.group_sale_director 1 0 0 0
access_mrp_unbuild_group_sale_salemanager mrp_unbuild_group_sale_salemanager mrp.model_mrp_unbuild sf_base.group_sale_salemanager 1 0 0 0
access_mrp_unbuild_group_sale_director mrp_unbuild_group_sale_director mrp.model_mrp_unbuild sf_base.group_sale_director 1 0 0 0
access_mrp_workcenter_productivity_group_sale_salemanager mrp_workcenter_productivity_group_sale_salemanager mrp.model_mrp_workcenter_productivity sf_base.group_sale_salemanager 1 0 0 0
access_mrp_workcenter_productivity_group_sale_director mrp_workcenter_productivity_group_sale_director mrp.model_mrp_workcenter_productivity sf_base.group_sale_director 1 0 0 0
access_sf_detection_result_group_sale_salemanager sf_detection_result_group_sale_salemanager sf_manufacturing.model_sf_detection_result sf_base.group_sale_salemanager 1 0 0 0
access_sf_detection_result_group_sale_director sf_detection_result_group_sale_director sf_manufacturing.model_sf_detection_result sf_base.group_sale_director 1 0 0 0
access_stock_scrap_group_sale_salemanager stock_scrap_group_sale_salemanager stock.model_stock_scrap sf_base.group_sale_salemanager 1 0 0 0
access_stock_scrap_group_sale_director stock_scrap_group_sale_director stock.model_stock_scrap sf_base.group_sale_director 1 0 0 0
99
100

View File

@@ -1,23 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<!-- 采购-产品 -->
<menuitem id="purchase.menu_purchase_products" name="Products" parent="purchase.menu_purchase_root"
groups="sf_base.group_purchase_director,sf_base.group_purchase"
sequence="5"/>
<!-- 采购-产品-产品 -->
<menuitem id="purchase.menu_procurement_partner_contact_form" name="Products"
action="purchase.product_normal_action_puchased" parent="purchase.menu_purchase_products"
groups="sf_base.group_purchase_director,sf_base.group_purchase"
sequence="20"/>
<!-- 采购-报表 -->
<menuitem id="purchase.purchase_report_main" name="Reporting" parent="purchase.menu_purchase_root" sequence="99"
groups="purchase.group_purchase_manager,sf_base.group_purchase_director,sf_base.group_purchase"/>
<!-- 采购-报表-采购 -->
<menuitem id="purchase.purchase_report" name="Purchase" parent="purchase.purchase_report_main" sequence="99"
groups="purchase.group_purchase_manager,sf_base.group_purchase_director,sf_base.group_purchase"
action="purchase.action_purchase_order_report_all"/>
</odoo>

View File

@@ -86,18 +86,6 @@
</attribute> </attribute>
</xpath> </xpath>
<xpath expr="//form/sheet/div[@name='button_box']/button[@name='action_view_picking']"
position="replace">
<button type="object"
name="action_view_picking"
class="oe_stat_button"
icon="fa-truck" attrs="{'invisible':[('incoming_picking_count','=', 0)]}"
groups="stock.group_stock_user,sf_base.group_purchase,sf_base.group_purchase_director">
<field name="incoming_picking_count" widget="statinfo" string="收货"
help="Incoming Shipments"/>
</button>
</xpath>
<xpath expr="//field[@name='order_line']" position="attributes"> <xpath expr="//field[@name='order_line']" position="attributes">
<attribute name="attrs">{'readonly': [('state', 'in', ['purchase'])]} <attribute name="attrs">{'readonly': [('state', 'in', ['purchase'])]}
</attribute> </attribute>

View File

@@ -6,14 +6,6 @@
<field name="model">sale.order</field> <field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_order_form"/> <field name="inherit_id" ref="sale.view_order_form"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<xpath expr="//button[@name='action_view_delivery']" position="attributes">
<attribute name="groups">sf_base.group_sale_salemanager,sf_base.group_sale_director</attribute>
</xpath>
<xpath expr="//button[@name='action_view_mrp_production']" position="attributes">
<attribute name="groups">
mrp.group_mrp_user,sf_base.group_sale_salemanager,sf_base.group_sale_director
</attribute>
</xpath>
<xpath expr="//field[@name='user_id']" position="replace"> <xpath expr="//field[@name='user_id']" position="replace">
<field name="user_id" widget="many2one_avatar_user" context="{'is_sale': True }"/> <field name="user_id" widget="many2one_avatar_user" context="{'is_sale': True }"/>
</xpath> </xpath>
@@ -42,15 +34,16 @@
<!-- </xpath>--> <!-- </xpath>-->
<xpath expr="//form/header/button[@name='action_confirm'][2]" position="replace"> <xpath expr="//form/header/button[@name='action_confirm'][2]" position="replace">
<button name="action_confirm" data-hotkey="v" <button name="action_confirm" data-hotkey="v"
string="确认接单" type="object" context="{'validate_analytic': True}" string="确认" type="object" context="{'validate_analytic': True}"
attrs="{'invisible': ['|', ('state', 'in', ['cancel']), '|','&amp;',('check_status', '!=', 'approved'),('state', 'in', ['draft','cancel']),'&amp;','&amp;',('check_status', '=', 'approved'),('state', 'in', ['sale','cancel']),('delivery_status', '!=', False)]}"/> attrs="{'invisible': ['|','&amp;',('check_status', '!=', 'approved'),('state', 'in', ['draft','cancel']),'&amp;','&amp;',('check_status', '=', 'approved'),('state', 'in', ['sale','cancel']),('delivery_status', '!=', False)]}"/>
</xpath> </xpath>
<xpath expr="//form/header/button[@name='action_cancel']" position="attributes"> <xpath expr="//form/header/button[@name='action_cancel']" position="attributes">
<attribute name="attrs">{'invisible': ['|', ('state', 'in', ['cancel']), '|','&amp;', <attribute name="attrs">{'invisible': ['|','&amp;',('state', 'in',
('check_status', '!=', 'approved'),('state', 'in', ['draft','cancel']),'&amp;','&amp;',('check_status', ['cancel','draft']),('check_status',
'=', 'approved'),('state', 'in', ['sale','cancel']),('delivery_status', '!=', False)]} 'in',
[False,'approved']),'&amp;',('check_status', '=', 'approved'),('state', 'in',
['sale','cancel','draft'])]}
</attribute> </attribute>
<attribute name="string">拒绝接单</attribute>
</xpath> </xpath>
<xpath expr="//form/header/button[@name='action_draft']" position="attributes"> <xpath expr="//form/header/button[@name='action_draft']" position="attributes">
<attribute name="invisible">1</attribute> <attribute name="invisible">1</attribute>
@@ -130,9 +123,6 @@
<field name="signature" position="attributes"> <field name="signature" position="attributes">
<attribute name="attrs">{'readonly': [('state', 'in', ['cancel','sale'])]}</attribute> <attribute name="attrs">{'readonly': [('state', 'in', ['cancel','sale'])]}</attribute>
</field> </field>
<xpath expr="//button[@name='action_cancel']" position="attributes">
<attribute name="string">拒绝接单</attribute>
</xpath>
</field> </field>
</record> </record>

View File

@@ -94,47 +94,47 @@ class MachineTableToolChangingApply(models.Model):
if len(records) > 1: if len(records) > 1:
raise ValidationError('该刀位号已存在,请重新选择!!!') raise ValidationError('该刀位号已存在,请重新选择!!!')
# @api.constrains('functional_tool_status') @api.constrains('functional_tool_status')
# def automation_apply_for_tool_change(self): def automation_apply_for_tool_change(self):
# """ """
# 自动申请换刀 自动申请换刀
# :return: :return:
# """ """
# # 更新数据到机台换刀申请界面 # 更新数据到机台换刀申请界面
# if self.functional_tool_status == '报警' and not self.sf_functional_tool_assembly_id: if self.functional_tool_status == '报警' and not self.sf_functional_tool_assembly_id:
# machine_table_tool_changing_apply = self.env['sf.machine.table.tool.changing.apply'].search( machine_table_tool_changing_apply = self.env['sf.machine.table.tool.changing.apply'].search(
# [('maintenance_equipment_id', '=', self.maintenance_equipment_id.id), [('maintenance_equipment_id', '=', self.maintenance_equipment_id.id),
# ('cutter_spacing_code_id', '=', self.cutter_spacing_code_id.id) ('cutter_spacing_code_id', '=', self.cutter_spacing_code_id.id)
# ]) ])
#
# # 创建功能刀具预警记录 # 创建功能刀具预警记录
# self.env['sf.functional.tool.warning'].create_tool_warning_record({'tool_changing_apply_id': self}) self.env['sf.functional.tool.warning'].create_tool_warning_record({'tool_changing_apply_id': self})
#
# # 新建组装任务 # 新建组装任务
# sf_functional_tool_assembly = self.env['sf.functional.tool.assembly'].create({ sf_functional_tool_assembly = self.env['sf.functional.tool.assembly'].create({
# 'functional_tool_name': self.functional_tool_name, 'functional_tool_name': self.functional_tool_name,
# 'functional_tool_type_id': self.functional_tool_type_id.id, 'functional_tool_type_id': self.functional_tool_type_id.id,
# 'functional_tool_diameter': self.diameter, 'functional_tool_diameter': self.diameter,
# 'knife_tip_r_angle': self.knife_tip_r_angle, 'knife_tip_r_angle': self.knife_tip_r_angle,
# 'coarse_middle_thin': '3', 'coarse_middle_thin': '3',
# 'new_former': '0', 'new_former': '0',
# 'functional_tool_length': self.extension_length, 'functional_tool_length': self.extension_length,
# 'effective_length': self.effective_length, 'effective_length': self.effective_length,
# 'loading_task_source': '1', 'loading_task_source': '1',
# 'use_tool_time': fields.Datetime.now() + timedelta(hours=4), 'use_tool_time': fields.Datetime.now() + timedelta(hours=4),
# 'production_line_name_id': self.production_line_id.id, 'production_line_name_id': self.production_line_id.id,
# 'machine_tool_name_id': self.maintenance_equipment_id.id, 'machine_tool_name_id': self.maintenance_equipment_id.id,
# 'applicant': '系统自动', 'applicant': '系统自动',
# 'apply_time': fields.Datetime.now(), 'apply_time': fields.Datetime.now(),
# 'cutter_spacing_code_id': self.cutter_spacing_code_id.id, 'cutter_spacing_code_id': self.cutter_spacing_code_id.id,
# 'whether_standard_knife': self.whether_standard_knife, 'whether_standard_knife': self.whether_standard_knife,
# 'reason_for_applying': '机台报警自动换刀', 'reason_for_applying': '机台报警自动换刀',
# 'sf_machine_table_tool_changing_apply_id': self.id 'sf_machine_table_tool_changing_apply_id': self.id
# }) })
#
# machine_table_tool_changing_apply.write( machine_table_tool_changing_apply.write(
# {'status': '1', {'status': '1',
# 'sf_functional_tool_assembly_id': sf_functional_tool_assembly.id}) 'sf_functional_tool_assembly_id': sf_functional_tool_assembly.id})
def revocation_1(self): def revocation_1(self):
""" """
@@ -760,15 +760,6 @@ class FunctionalToolDismantle(models.Model):
functional_tool_id = fields.Many2one('sf.functional.cutting.tool.entity', '功能刀具', required=True, tracking=True, functional_tool_id = fields.Many2one('sf.functional.cutting.tool.entity', '功能刀具', required=True, tracking=True,
domain=[('functional_tool_status', '!=', '已拆除'), domain=[('functional_tool_status', '!=', '已拆除'),
('current_location', '=', '刀具房')]) ('current_location', '=', '刀具房')])
@api.onchange('functional_tool_id')
def _onchange_functional_tool_id(self):
for item in self:
if item:
dismantle_id = self.search([('functional_tool_id', '=', item.functional_tool_id.id)])
if dismantle_id:
raise ValidationError(f'Rfid为【{item.rfid}】的功能刀具已经存在拆解单,单号是【{dismantle_id[0].code}')
tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型', store=True, tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型', store=True,
compute='_compute_functional_tool_num') compute='_compute_functional_tool_num')
tool_groups_id = fields.Many2one('sf.tool.groups', '刀具组', compute='_compute_functional_tool_num', store=True) tool_groups_id = fields.Many2one('sf.tool.groups', '刀具组', compute='_compute_functional_tool_num', store=True)
@@ -947,23 +938,15 @@ class FunctionalToolDismantle(models.Model):
if self.chuck_freight_id == self.pad_freight_id: if self.chuck_freight_id == self.pad_freight_id:
raise ValidationError('【夹头】和【刀盘】的目标货位重复,请重新选择!') raise ValidationError('【夹头】和【刀盘】的目标货位重复,请重新选择!')
def tool_scrap(self):
self.scrap_boolean = True
def tool_no_scrap(self):
self.scrap_boolean = False
def confirmation_disassembly(self): def confirmation_disassembly(self):
logging.info('%s刀具确认开始拆解' % self.dismantle_cause) logging.info('%s刀具确认开始拆解' % self.dismantle_cause)
code = self.code code = self.code
if self.functional_tool_id.functional_tool_status == '已拆除': if self.functional_tool_id.functional_tool_status == '已拆除':
raise ValidationError('Rfid为【%s名称为【%s的功能刀具已经拆解,请勿重复操作!' % ( raise ValidationError('Rfid为【%s】的功能刀具已经拆解,请勿重复操作!' % self.functional_tool_id.rfid_dismantle)
self.functional_tool_id.rfid_dismantle, self.name))
# 对拆解的功能刀具进行校验,只有在刀具房的功能刀具才能拆解 # 对拆解的功能刀具进行校验,只有在刀具房的功能刀具才能拆解
elif self.functional_tool_id.functional_tool_status != '报警': if self.functional_tool_id.tool_room_num == 0:
if self.functional_tool_id.tool_room_num == 0: raise ValidationError('Rfid为【%s】的功能刀具当前位置为【%s】,不能进行拆解!' % (
raise ValidationError('Rfid为【%s】的功能刀具当前位置为【%s】,不能进行拆解!' % ( self.rfid, self.functional_tool_id.current_location))
self.rfid, self.functional_tool_id.current_location))
# 目标重复校验 # 目标重复校验
self.location_duplicate_check() self.location_duplicate_check()
datas = {'scrap': [], 'picking': []} datas = {'scrap': [], 'picking': []}
@@ -1022,14 +1005,6 @@ class FunctionalToolDismantle(models.Model):
'rfid': '%s(已拆解)' % self.rfid, 'rfid': '%s(已拆解)' % self.rfid,
'state': '已拆解' 'state': '已拆解'
}) })
# ==================修改刀具预警信息的值============
warning_id = self.env['sf.functional.tool.warning'].sudo().search(
[('functional_tool_id', '=', self.functional_tool_id.id)])
if warning_id:
warning_id.sudo().write({
'dispose_user': self.env.user.name,
'dispose_time': fields.Datetime.now()
})
logging.info('%s】刀具拆解成功!' % self.name) logging.info('%s】刀具拆解成功!' % self.name)
def create_tool_picking_scrap(self, datas): def create_tool_picking_scrap(self, datas):
@@ -1128,7 +1103,7 @@ class StockMove(models.Model):
move_line_ids = picking_id.move_line_ids move_line_ids = picking_id.move_line_ids
for move_line_id in move_line_ids: for move_line_id in move_line_ids:
for res in data: for res in data:
if move_line_id.product_id == res['lot_id'].product_id: if move_line_id.lot_id.product_id == res['lot_id'].product_id:
move_line_id.write({ move_line_id.write({
'destination_location_id': res.get('destination').id, 'destination_location_id': res.get('destination').id,
'lot_id': res.get('lot_id').id 'lot_id': res.get('lot_id').id

View File

@@ -11,7 +11,6 @@ from odoo.exceptions import ValidationError
class FunctionalCuttingToolEntity(models.Model): class FunctionalCuttingToolEntity(models.Model):
_name = 'sf.functional.cutting.tool.entity' _name = 'sf.functional.cutting.tool.entity'
_description = '功能刀具列表' _description = '功能刀具列表'
_order = 'functional_tool_status'
functional_tool_name_id = fields.Many2one('sf.functional.tool.assembly', string='功能刀具组装单', readonly=True) functional_tool_name_id = fields.Many2one('sf.functional.tool.assembly', string='功能刀具组装单', readonly=True)
@@ -54,22 +53,6 @@ class FunctionalCuttingToolEntity(models.Model):
safe_inventory_id = fields.Many2one('sf.real.time.distribution.of.functional.tools', safe_inventory_id = fields.Many2one('sf.real.time.distribution.of.functional.tools',
string='功能刀具安全库存', readonly=True) string='功能刀具安全库存', readonly=True)
@api.onchange('functional_tool_status')
def _onchange_functional_tool_status(self):
for item in self:
if item:
if item.functional_tool_status == '报警':
# 创建报警刀具拆解单
self.env['sf.functional.tool.dismantle'].sudo().create({
'functional_tool_id': item.ids[0],
'dismantle_cause': '寿命到期报废'
})
# 创建刀具报警记录
self.env['sf.functional.tool.warning'].sudo().create({
'rfid': item.rfid,
'functional_tool_id': item.ids[0]
})
@api.depends('barcode_id.quant_ids', 'barcode_id.quant_ids.location_id', 'functional_tool_status', @api.depends('barcode_id.quant_ids', 'barcode_id.quant_ids.location_id', 'functional_tool_status',
'current_shelf_location_id') 'current_shelf_location_id')
def _compute_current_location_id(self): def _compute_current_location_id(self):
@@ -118,28 +101,27 @@ class FunctionalCuttingToolEntity(models.Model):
def tool_in_out_stock_location(self, location_id): def tool_in_out_stock_location(self, location_id):
tool_room_id = self.env['stock.location'].search([('name', '=', '刀具房')]) tool_room_id = self.env['stock.location'].search([('name', '=', '刀具房')])
pre_manufacturing_id = self.env['stock.location'].search([('name', '=', '制造前')]) pre_manufacturing_id = self.env['stock.location'].search([('name', '=', '制造前')])
if self: for item in self:
for item in self: # 中控反馈该位置有刀
# 中控反馈该位置有刀 if item:
if item: # 系统该位置有刀
# 系统该位置有刀 if location_id.product_sn_id:
if location_id.product_sn_id: # 中控反馈和系统中,该位置是同一把刀
# 中控反馈和系统中,该位置是同一把刀 if item.barcode_id == location_id.product_sn_id:
if item.barcode_id == location_id.product_sn_id: return True
return True # 中控反馈和系统中,该位置不是同一把刀
# 中控反馈和系统中,该位置不是同一把刀 else:
else: # 原刀从线边出库
# 原刀从线边出库 item.tool_in_out_stock_location_1(location_id, tool_room_id)
item.tool_in_out_stock_location_1(location_id, tool_room_id) # 新刀入库到线边
# 新刀入库到线边 item.create_stock_move(pre_manufacturing_id, location_id)
item.create_stock_move(pre_manufacturing_id, location_id) item.current_shelf_location_id = location_id.id
item.current_shelf_location_id = location_id.id
# 中控反馈该位置没有刀 # 中控反馈该位置没有刀
else: else:
# 系统该位置有刀 # 系统该位置有刀
if location_id.product_sn_id: if location_id.product_sn_id:
self.tool_in_out_stock_location_1(location_id, tool_room_id) item.tool_in_out_stock_location_1(location_id, tool_room_id)
def tool_in_out_stock_location_1(self, location_id, tool_room_id): def tool_in_out_stock_location_1(self, location_id, tool_room_id):
tool = self.env['sf.functional.cutting.tool.entity'].search( tool = self.env['sf.functional.cutting.tool.entity'].search(
@@ -256,39 +238,10 @@ class FunctionalCuttingToolEntity(models.Model):
functional_tool_model_ids.append(functional_tool_model.id) functional_tool_model_ids.append(functional_tool_model.id)
return [(6, 0, functional_tool_model_ids)] return [(6, 0, functional_tool_model_ids)]
dismantle_num = fields.Integer('拆解单数量', compute='_compute_dismantle_num', store=True)
dismantle_ids = fields.One2many('sf.functional.tool.dismantle', 'functional_tool_id', '拆解单')
@api.depends('dismantle_ids')
def _compute_dismantle_num(self):
for item in self:
if item:
item.dismantle_num = len(item.dismantle_ids)
def open_functional_tool_dismantle_form(self):
self.ensure_one()
dismantle_ids = self.env['sf.functional.tool.dismantle'].sudo().search([('functional_tool_id', '=', self.id)])
action = {
'res_model': 'sf.functional.tool.dismantle',
'type': 'ir.actions.act_window',
'name': '拆解单',
}
if len(dismantle_ids) == 1:
action.update({
'view_mode': 'form',
'res_id': dismantle_ids[0].id,
})
else:
action.update({
'domain': [('id', 'in', dismantle_ids.ids)],
'view_mode': 'tree,form',
})
return action
def open_functional_tool_warning(self): def open_functional_tool_warning(self):
action = self.env.ref('sf_tool_management.action_sf_functional_tool_warning') action = self.env.ref('sf_tool_management.action_sf_functional_tool_warning')
result = action.read()[0] result = action.read()[0]
result['domain'] = [('functional_tool_id', '=', self.id)] result['domain'] = [('functional_tool_name_id', '=', self.functional_tool_name_id.id)]
return result return result
def open_stock_move_line(self): def open_stock_move_line(self):
@@ -370,10 +323,10 @@ class FunctionalToolWarning(models.Model):
_name = 'sf.functional.tool.warning' _name = 'sf.functional.tool.warning'
_description = '功能刀具预警' _description = '功能刀具预警'
code = fields.Char('编码', related='functional_tool_id.code') code = fields.Char('编码', related='functional_tool_name_id.code')
rfid = fields.Char('Rfid', readonly=True) rfid = fields.Char('Rfid', related='functional_tool_name_id.rfid')
tool_groups_id = fields.Many2one('sf.tool.groups', '刀具组', related='functional_tool_id.tool_groups_id') tool_groups_id = fields.Many2one('sf.tool.groups', '刀具组', related='functional_tool_name_id.tool_groups_id')
name = fields.Char('名称', invisible=True, readonly=True, related='functional_tool_id.name') name = fields.Char('名称', invisible=True, readonly=True, related='functional_tool_name_id.name')
# 机床信息 # 机床信息
production_line_id = fields.Many2one('sf.production.line', string='生产线', production_line_id = fields.Many2one('sf.production.line', string='生产线',
group_expand='_read_group_machine_table_name_ids') group_expand='_read_group_machine_table_name_ids')
@@ -384,77 +337,52 @@ class FunctionalToolWarning(models.Model):
cutter_spacing_code_id = fields.Many2one('maintenance.equipment.tool', string='刀位号', cutter_spacing_code_id = fields.Many2one('maintenance.equipment.tool', string='刀位号',
domain="[('equipment_id', '=', maintenance_equipment_id)]") domain="[('equipment_id', '=', maintenance_equipment_id)]")
# 功能刀具信息 # 功能刀具信息
functional_tool_id = fields.Many2one('sf.functional.cutting.tool.entity', string='功能刀具', readonly=True) functional_tool_name_id = fields.Many2one('sf.functional.tool.assembly', string='功能刀具名称')
barcode_id = fields.Many2one('stock.lot', string='序列号', related='functional_tool_id.barcode_id') barcode_id = fields.Many2one('stock.lot', string='功能刀具序列号', related='functional_tool_name_id.barcode_id')
mrs_cutting_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型', mrs_cutting_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型')
related='functional_tool_id.sf_cutting_tool_type_id') diameter = fields.Float(string='刀具直径(mm)')
diameter = fields.Float(string='具直径(mm)', related='functional_tool_id.functional_tool_diameter') knife_tip_r_angle = fields.Float(string='尖R角(mm)')
knife_tip_r_angle = fields.Float(string='刀尖R角(mm)', related='functional_tool_id.knife_tip_r_angle')
# 其他信息 # 其他信息
install_tool_time = fields.Datetime("刀具组装时间", related='functional_tool_name_id.tool_loading_time') install_tool_time = fields.Datetime("刀具组装时间", related='functional_tool_name_id.tool_loading_time')
on_board_time = fields.Datetime('上机装刀时间') on_board_time = fields.Datetime('上机装刀时间')
max_lifetime_value = fields.Integer(string='最大寿命值(min)', related='functional_tool_id.max_lifetime_value') max_lifetime_value = fields.Integer(string='最大寿命值(min)')
alarm_value = fields.Integer(string='报警值(min)', related='functional_tool_id.alarm_value') alarm_value = fields.Integer(string='报警值(min)')
used_value = fields.Integer(string='已使用值(min)', related='functional_tool_id.used_value') used_value = fields.Integer(string='已使用值(min)')
functional_tool_status = fields.Selection([('正常', '正常'), ('报警', '报警'), ('已拆除', '已拆除')], string='状态') functional_tool_status = fields.Selection([('正常', '正常'), ('报警', '报警'), ('已拆除', '已拆除')], string='状态')
alarm_time = fields.Datetime('报警时间', default=lambda self: fields.Datetime.now(), readonly=True) alarm_time = fields.Datetime('报警时间')
dispose_user = fields.Char('处理人', readonly=True) dispose_user = fields.Char('处理人')
dispose_time = fields.Char('处理时间', readonly=True) dispose_time = fields.Char('处理时间')
dispose_func = fields.Char('处理方法/措施', readonly=True) dispose_func = fields.Char('处理方法/措施', readonly=False)
active = fields.Boolean(string='已归档', default=True) active = fields.Boolean(string='已归档', default=True)
functional_tool_name_id = fields.Many2one('sf.functional.tool.assembly', string='功能刀具名称')
@api.model @api.model
def _read_group_machine_table_name_ids(self, categories, domain, order): def _read_group_machine_table_name_ids(self, categories, domain, order):
machine_table_name_ids = categories._search([], order=order, access_rights_uid=SUPERUSER_ID) machine_table_name_ids = categories._search([], order=order, access_rights_uid=SUPERUSER_ID)
return categories.browse(machine_table_name_ids) return categories.browse(machine_table_name_ids)
def action_open_dismantle(self): def create_tool_warning_record(self, obj):
self.ensure_one() """
dismantle_ids = self.env['sf.functional.tool.dismantle'].sudo().search( 机台换刀申请报警状态时,创建功能刀具预警记录
[('functional_tool_id', '=', self.functional_tool_id.id)]) """
action = { if obj:
'res_model': 'sf.functional.tool.dismantle', for tool in obj.get('tool_changing_apply_id'):
'type': 'ir.actions.act_window', self.env['sf.functional.tool.warning'].create({
'name': '拆解单' 'production_line_id': tool.production_line_id.id,
} 'maintenance_equipment_id': tool.maintenance_equipment_id.id,
if len(dismantle_ids) == 1: 'machine_tool_code': tool.machine_tool_code,
action.update({ 'machine_table_type_id': tool.machine_table_type_id.id,
'view_mode': 'form', 'cutter_spacing_code_id': tool.cutter_spacing_code_id.id,
'res_id': dismantle_ids.ids[0] 'functional_tool_name_id': tool.functional_tool_name_id.id,
}) 'barcode_id': tool.barcode_id.id,
elif dismantle_ids: 'diameter': tool.diameter,
action.update({ 'knife_tip_r_angle': tool.knife_tip_r_angle,
'view_mode': 'tree,form', 'max_lifetime_value': tool.max_lifetime_value,
'domain': [('id', 'in', dismantle_ids.ids)], 'alarm_value': tool.alarm_value,
}) 'used_value': tool.used_value,
else: 'functional_tool_status': tool.functional_tool_status,
return False 'alarm_time': fields.Datetime.now(),
return action })
# def create_tool_warning_record(self, obj):
# """
# 机台换刀申请报警状态时,创建功能刀具预警记录
# """
# if obj:
# for tool in obj.get('tool_changing_apply_id'):
# self.env['sf.functional.tool.warning'].create({
# 'production_line_id': tool.production_line_id.id,
# 'maintenance_equipment_id': tool.maintenance_equipment_id.id,
# 'machine_tool_code': tool.machine_tool_code,
# 'machine_table_type_id': tool.machine_table_type_id.id,
# 'cutter_spacing_code_id': tool.cutter_spacing_code_id.id,
# 'functional_tool_name_id': tool.functional_tool_name_id.id,
# 'barcode_id': tool.barcode_id.id,
# 'diameter': tool.diameter,
# 'knife_tip_r_angle': tool.knife_tip_r_angle,
# 'max_lifetime_value': tool.max_lifetime_value,
# 'alarm_value': tool.alarm_value,
# 'used_value': tool.used_value,
# 'functional_tool_status': tool.functional_tool_status,
# 'alarm_time': fields.Datetime.now(),
# })
class StockMoveLine(models.Model): class StockMoveLine(models.Model):

View File

@@ -53,7 +53,7 @@ class SfMaintenanceEquipment(models.Model):
params = {"DeviceId": self.name} params = {"DeviceId": self.name}
r = requests.get(crea_url, params=params, headers=headers) r = requests.get(crea_url, params=params, headers=headers)
ret = r.json() ret = r.json()
logging.info('机床刀库register_equipment_tool():%s' % ret) logging.info('register_equipment_tool:%s' % ret)
datas = ret['Datas'] datas = ret['Datas']
self.write_maintenance_equipment_tool(datas) self.write_maintenance_equipment_tool(datas)
if ret['Succeed']: if ret['Succeed']:

View File

@@ -25,10 +25,7 @@
<field name="max_lifetime_value"/> <field name="max_lifetime_value"/>
<field name="alarm_value"/> <field name="alarm_value"/>
<field name="used_value"/> <field name="used_value"/>
<field name="functional_tool_status" widget='badge' <field name="functional_tool_status"/>
decoration-success="functional_tool_status == '正常'"
decoration-muted="functional_tool_status == '已拆除'"
decoration-danger="functional_tool_status == '报警'"/>
<field name="current_location" string="当前位置"/> <field name="current_location" string="当前位置"/>
<field name="current_location_id" invisible="1"/> <field name="current_location_id" invisible="1"/>
@@ -51,18 +48,6 @@
<div class="oe_button_box" name="button_box"> <div class="oe_button_box" name="button_box">
<!-- <button name="button_safe_inventory_id" string="更新功能刀具关联的安全库存记录"--> <!-- <button name="button_safe_inventory_id" string="更新功能刀具关联的安全库存记录"-->
<!-- type="object" class="btn-primary"/>--> <!-- type="object" class="btn-primary"/>-->
<button class="oe_stat_button" groups="sf_base.group_sf_mrp_user"
name="open_functional_tool_dismantle_form"
icon="fa-credit-card"
type="object"
attrs="{'invisible': [('dismantle_num', '=', 0)]}">
<div name="dismantle_num" class="o_field_widget o_readonly_modifier o_field_statinfo">
<span class="o_stat_info o_stat_value">
<field name="dismantle_num"/>
</span>
<span class="o_stat_text">拆解单</span>
</div>
</button>
<button class="oe_stat_button" groups="sf_base.group_sf_mrp_user" <button class="oe_stat_button" groups="sf_base.group_sf_mrp_user"
name="open_functional_tool_warning" name="open_functional_tool_warning"
icon="fa-list-ul" icon="fa-list-ul"
@@ -250,29 +235,26 @@
<field name="name">sf.functional.tool.warning.tree</field> <field name="name">sf.functional.tool.warning.tree</field>
<field name="model">sf.functional.tool.warning</field> <field name="model">sf.functional.tool.warning</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree string="功能刀具预警" create="0" edit="0" delete="0" editable="bottom" default_order="id desc" <tree string="功能刀具预警" create="0" edit="0" delete="0" editable="bottom">
action="action_open_dismantle" type="object"> <field name="production_line_id" optional="hide"/>
<field name="production_line_id" invisible="1"/> <field name="maintenance_equipment_id" optional="hide"/>
<field name="maintenance_equipment_id" invisible="1"/> <field name="machine_tool_code"/>
<field name="machine_tool_code" invisible="1"/> <field name="cutter_spacing_code_id"/>
<field name="cutter_spacing_code_id" invisible="1"/> <field name="barcode_id" invisible="1"/>
<field name="on_board_time" invisible="1"/>
<field name="functional_tool_status" invisible="1"/>
<field name="functional_tool_name_id" invisible="1"/>
<field name="rfid"/> <field name="rfid"/>
<field name="functional_tool_id"/> <field name="functional_tool_name_id"/>
<field name="barcode_id" optional="hide"/> <field name="diameter"/>
<field name="diameter" optional="hide"/> <field name="knife_tip_r_angle"/>
<field name="knife_tip_r_angle" optional="hide"/>
<field name="install_tool_time" optional="hide"/> <field name="install_tool_time" optional="hide"/>
<field name="on_board_time" optional="hide"/>
<field name="max_lifetime_value"/> <field name="max_lifetime_value"/>
<field name="alarm_value"/> <field name="alarm_value"/>
<field name="used_value"/> <field name="used_value"/>
<field name="functional_tool_status"/>
<field name="alarm_time"/> <field name="alarm_time"/>
<field name="dispose_user"/> <field name="dispose_user"/>
<field name="dispose_time"/> <field name="dispose_time"/>
<field name="dispose_func" optional="hide"/> <field name="dispose_func"/>
<!-- <button name="enroll_functional_tool_warning" string="刀具预警注册" type="object"--> <!-- <button name="enroll_functional_tool_warning" string="刀具预警注册" type="object"-->
<!-- class="btn-primary"/>--> <!-- class="btn-primary"/>-->
</tree> </tree>
@@ -284,19 +266,31 @@
<field name="model">sf.functional.tool.warning</field> <field name="model">sf.functional.tool.warning</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<search string="功能刀具预警"> <search string="功能刀具预警">
<field name="machine_tool_code"/>
<field name="cutter_spacing_code_id"/>
<field name="barcode_id"/> <field name="barcode_id"/>
<field name="rfid"/> <field name="rfid"/>
<field name="functional_tool_name_id"/> <field name="functional_tool_name_id"/>
<field name="diameter"/>
<field name="knife_tip_r_angle"/>
<field name="install_tool_time" optional="hide"/>
<field name="on_board_time" optional="hide"/>
<field name="max_lifetime_value"/>
<field name="alarm_value"/>
<field name="used_value"/>
<field name="functional_tool_status"/>
<field name="alarm_time"/>
<field name="dispose_user"/>
<field name="dispose_time"/>
<field name="dispose_func"/>
<field name="production_line_id" invisible="True"/>
<filter string="已归档" name="inactive" domain="[('active', '=', False)]"/>
<searchpanel> <searchpanel>
<field name="production_line_id" icon="fa-building" enable_counters="1"/> <field name="production_line_id" icon="fa-building" enable_counters="1"/>
<field name="maintenance_equipment_id" icon="fa-building" enable_counters="1"/> <field name="maintenance_equipment_id" icon="fa-building" enable_counters="1"/>
<field name="cutter_spacing_code_id" icon="fa-building" enable_counters="1"/> <field name="cutter_spacing_code_id" icon="fa-building" enable_counters="1"/>
<field name="functional_tool_status" icon="fa-building" enable_counters="1"/> <field name="functional_tool_status" icon="fa-building" enable_counters="1"/>
</searchpanel> </searchpanel>
<group expand="0">
<filter string="报警时间" name="alarm_time" domain="[]"
context="{'group_by': 'alarm_time'}"/>
</group>
</search> </search>
</field> </field>
</record> </record>

View File

@@ -704,10 +704,10 @@
<field name="model">sf.functional.tool.assembly</field> <field name="model">sf.functional.tool.assembly</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<search> <search>
<field name="functional_tool_name"/>
<field name="assembly_order_code"/> <field name="assembly_order_code"/>
<field name="code" string="功能刀具编码"/> <field name="code" string="功能刀具编码"/>
<field name="barcode_id"/> <field name="barcode_id"/>
<field name="functional_tool_name"/>
<field name="functional_tool_type_id"/> <field name="functional_tool_type_id"/>
<field name="tool_groups_id"/> <field name="tool_groups_id"/>
<field name="loading_task_source" string="任务来源"/> <field name="loading_task_source" string="任务来源"/>
@@ -800,16 +800,10 @@
</h1> </h1>
</div> </div>
<field name="_barcode_scanned" widget="barcode_handler"/> <field name="_barcode_scanned" widget="barcode_handler"/>
<script>
setTimeout(function(){
$('#functional_tool_id').blur()
}, 100)
</script>
<group> <group>
<group> <group>
<field name="functional_tool_id" placeholder="请选择将要拆解的功能刀具" <field name="functional_tool_id" placeholder="请选择将要拆解的功能刀具"
options="{'no_create': True}" options="{'no_create': True}" attrs="{'readonly': [('state', '=', '已拆解')]}"/>
attrs="{'readonly': ['|',('state', '=', '已拆解'),('id', '!=', False)]}"/>
<field name="rfid" attrs="{'invisible': [('rfid', '=', '')]}"/> <field name="rfid" attrs="{'invisible': [('rfid', '=', '')]}"/>
<field name="rfid_dismantle" attrs="{'invisible': [('rfid_dismantle', '=', False)]}"/> <field name="rfid_dismantle" attrs="{'invisible': [('rfid_dismantle', '=', False)]}"/>
<field name="tool_type_id"/> <field name="tool_type_id"/>
@@ -839,26 +833,10 @@
<notebook> <notebook>
<page string="物料组装信息"> <page string="物料组装信息">
<group> <group>
<group string="刀柄" attrs="{'invisible': [('handle_product_id', '=', False)]}" <group string="刀柄" attrs="{'invisible': [('handle_product_id', '=', False)]}">
col="1">
<group attrs="{'invisible': [('dismantle_cause', 'not in', ['寿命到期报废','崩刀报废'])]}">
<!-- <group col="3">-->
<group>
<field name="scrap_boolean" string="是否报废" readonly="0"/>
</group>
<!-- <group></group>-->
<!-- <group>-->
<!-- <button string="报废" name="tool_scrap" type="object"-->
<!-- class="btn-primary" confirm="是否确认报废刀柄"-->
<!-- attrs="{'invisible': [('scrap_boolean', '=', True)]}"/>-->
<!-- <button string="取消" name="tool_no_scrap" type="object"-->
<!-- class="btn-primary" confirm="是否取消报废刀柄"-->
<!-- attrs="{'invisible': [('scrap_boolean', '=', False)]}"/>-->
<!-- <group></group>-->
<!-- </group>-->
<!-- </group>-->
</group>
<group> <group>
<field name="scrap_boolean" string="是否报废"
attrs="{'invisible': [('dismantle_cause', 'not in', ['寿命到期报废','崩刀报废'])], 'readonly': [('state', '=', '已拆解')]}"/>
<field name="handle_rfid" string="Rfid"/> <field name="handle_rfid" string="Rfid"/>
<field name="handle_lot_id" string="序列号"/> <field name="handle_lot_id" string="序列号"/>
<field name="handle_product_id" string="名称"/> <field name="handle_product_id" string="名称"/>
@@ -933,7 +911,7 @@
</group> </group>
</group> </group>
</page> </page>
<page string="报废" <page string="报废"
attrs="{'invisible':[('dismantle_cause', 'not in', ['寿命到期报废','崩刀报废'])]}"> attrs="{'invisible':[('dismantle_cause', 'not in', ['寿命到期报废','崩刀报废'])]}">
<field name="scrap_ids"> <field name="scrap_ids">
<tree> <tree>
@@ -974,9 +952,8 @@
<field name="model">sf.functional.tool.dismantle</field> <field name="model">sf.functional.tool.dismantle</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<search> <search>
<field name="rfid"/>
<field name="functional_tool_id"/> <field name="functional_tool_id"/>
<field name="code" string="拆解单"/> <field name="code" string="拆解单编码"/>
<filter name="no_dismantle_state" string="未拆解" domain="[('state','!=','已拆解')]"/> <filter name="no_dismantle_state" string="未拆解" domain="[('state','!=','已拆解')]"/>
<filter name="dismantle_state" string="已拆解" domain="[('state','=','已拆解')]"/> <filter name="dismantle_state" string="已拆解" domain="[('state','=','已拆解')]"/>
<separator/> <separator/>

View File

@@ -387,9 +387,9 @@ class FunctionalToolAssemblyOrder(models.TransientModel):
lot_ids = self.env['stock.lot'].sudo().search([('rfid', '=', barcode)]) lot_ids = self.env['stock.lot'].sudo().search([('rfid', '=', barcode)])
if lot_ids: if lot_ids:
for lot_id in lot_ids: for lot_id in lot_ids:
if lot_id.tool_material_status == '可用': if lot_id.quant_ids[-1].location_id.name in '刀具房':
record.handle_code_id = lot_id.id record.handle_code_id = lot_id.id
elif lot_id.quant_ids[-1].location_id.name in ['刀具组装位置']: elif lot_id.quant_ids[-1].location_id.name == '刀具组装位置':
raise ValidationError('该刀柄已使用,请重新扫描!!!') raise ValidationError('该刀柄已使用,请重新扫描!!!')
else: else:
raise ValidationError('该刀柄未入库,请重新扫描!!!') raise ValidationError('该刀柄未入库,请重新扫描!!!')
@@ -842,8 +842,6 @@ class StockPicking(models.Model):
stock_move_id = self.env['stock.move'] stock_move_id = self.env['stock.move']
datas = {'data': [], 'picking_id': picking_id} datas = {'data': [], 'picking_id': picking_id}
if obj.handle_code_id: if obj.handle_code_id:
# 修改刀柄序列号状态为【在用】
obj.handle_code_id.sudo().write({'tool_material_status': '在用'})
datas['data'].append( datas['data'].append(
{'current_location_id': self.env['sf.shelf.location'], 'lot_id': obj.handle_code_id}) {'current_location_id': self.env['sf.shelf.location'], 'lot_id': obj.handle_code_id})
if obj.integral_product_id: if obj.integral_product_id:

View File

@@ -201,11 +201,6 @@
</group> </group>
</group> </group>
</group> </group>
<script>
setTimeout(function(){
$('#handle_code_id').blur()
}, 100)
</script>
<group string="组装物料信息" col="1"> <group string="组装物料信息" col="1">
<field name="_barcode_scanned" widget="barcode_handler"/> <field name="_barcode_scanned" widget="barcode_handler"/>
<group col="1"> <group col="1">
@@ -373,7 +368,7 @@
<group> <group>
<field name="obtain_measurement_status" invisible="1"/> <field name="obtain_measurement_status" invisible="1"/>
<button name="get_tool_preset_parameter" string="获取测量值" type="object" <button name="get_tool_preset_parameter" string="获取测量值" type="object"
attrs="{'invisible': [('enable_tool_presetter', '=', False)]}" attrs="{'invisible': [('enable_tool_presetter', '=', False)]}"
class="btn-primary"/> class="btn-primary"/>
</group> </group>
</group> </group>

View File

@@ -507,13 +507,13 @@ class ShelfLocation(models.Model):
print('eeeeeee空闲', e) print('eeeeeee空闲', e)
# 调取获取货位信息接口 # 调取获取货位信息接口
def get_sf_shelf_location_info(self, device_id='Cabinet-AL'): def get_sf_shelf_location_info(self):
config = self.env['res.config.settings'].get_values() config = self.env['res.config.settings'].get_values()
headers = {'Authorization': config['center_control_Authorization']} headers = {'Authorization': config['center_control_Authorization']}
crea_url = config['center_control_url'] + "/AutoDeviceApi/GetLocationInfos" crea_url = config['center_control_url'] + "/AutoDeviceApi/GetLocationInfos"
params = {'DeviceId': device_id} params = {'DeviceId': 'Cabinet-AL'}
r = requests.get(crea_url, params=params, headers=headers) r = requests.get(crea_url, params=params, headers=headers)
ret = r.json() ret = r.json()

View File

@@ -13,92 +13,88 @@ class MrsShelfLocationDataSync(models.Model):
_name = 'sf.shelf.location.datasync' _name = 'sf.shelf.location.datasync'
_description = '同步库存信息' _description = '同步库存信息'
def get_total_data(self):
# 建立对应关系的函数
def align_data(my_data, their_data):
paired_data = list(zip(my_data, their_data))
return paired_data
logging.info('============================get_total_data()======================')
shelf_1_obj = self.env['sf.shelf'].search([('name', '=', '一号产线-一号线边刀架')], limit=1)
tool_location_objs_1 = self.env['sf.shelf.location'].search([('shelf_id', '=', shelf_1_obj.id)], order='id')
location_codes_1 = [location.barcode for location in tool_location_objs_1]
print(location_codes_1)
# 对方的数据列表
their_data_1 = [f"ToolCab1-{i:02}" for i in range(1, 73)]
# 执行对齐
aligned_data_1 = align_data(location_codes_1, their_data_1)
# 2
shelf_2_obj = self.env['sf.shelf'].search([('name', '=', '一号产线-二号线边刀架')], limit=1)
tool_location_objs_2 = self.env['sf.shelf.location'].search([('shelf_id', '=', shelf_2_obj.id)], order='id')
location_codes_2 = [location.barcode for location in tool_location_objs_2]
print(location_codes_2)
# 对方的数据列表
their_data_2 = [f"ToolCab2-{i:02}" for i in range(1, 73)]
# 执行对齐
aligned_data_2 = align_data(location_codes_2, their_data_2)
# 4
shelf_4_obj = self.env['sf.shelf'].search([('name', '=', '一号产线-一号线边料架')], limit=1)
tool_location_objs_4 = self.env['sf.shelf.location'].search([('shelf_id', '=', shelf_4_obj.id)], order='id')
location_codes_4 = [location.barcode for location in tool_location_objs_4]
print(location_codes_4)
# 对方的数据列表
their_data_4 = [f"PartCab4-{i:02}" for i in range(1, 17)]
# 执行对齐
aligned_data_4 = align_data(location_codes_4, their_data_4)
# 3
shelf_3_obj = self.env['sf.shelf'].search([('name', '=', '一号产线-二号线边料架')], limit=1)
tool_location_objs_3 = self.env['sf.shelf.location'].search([('shelf_id', '=', shelf_3_obj.id)], order='id')
location_codes_3 = [location.barcode for location in tool_location_objs_3]
print(location_codes_3)
# 对方的数据列表
their_data_3 = [f"PartCab3-{i:02}" for i in range(1, 13)]
# 执行对齐
aligned_data_3 = align_data(location_codes_3, their_data_3)
# 5
shelf_5_obj = self.env['sf.shelf'].search([('name', '=', '一号产线-三号线边料架')], limit=1)
tool_location_objs_5 = self.env['sf.shelf.location'].search([('shelf_id', '=', shelf_5_obj.id)], order='id')
location_codes_5 = [location.barcode for location in tool_location_objs_5]
print(location_codes_5)
# 对方的数据列表
their_data_5 = [f"PartCab5-{i:02}" for i in range(1, 13)]
# 执行对齐
aligned_data_5 = align_data(location_codes_5, their_data_5)
total_data = aligned_data_1 + aligned_data_2 + aligned_data_3 + aligned_data_4 + aligned_data_5
print(total_data)
logging.info(f"total_data: {total_data}")
return total_data
def find_our_code(self, total_data, their_code):
for code_pair in total_data:
if code_pair[1] == their_code:
return code_pair[0]
return None # 如果没有找到对应的值返回None或适当的默认值
def _cron_shelf_location_datasync(self): def _cron_shelf_location_datasync(self):
try: try:
# 建立对应关系的函数
def align_data(my_data, their_data):
paired_data = list(zip(my_data, their_data))
return paired_data
shelf_1_obj = self.env['sf.shelf'].search([('name', '=', '一号产线-一号线边刀架')], limit=1)
tool_location_objs_1 = self.env['sf.shelf.location'].search([('shelf_id', '=', shelf_1_obj.id)], order='id')
location_codes_1 = [location.barcode for location in tool_location_objs_1]
print(location_codes_1)
# 对方的数据列表
their_data_1 = [f"ToolCab1-{i:02}" for i in range(1, 73)]
# 执行对齐
aligned_data_1 = align_data(location_codes_1, their_data_1)
# 2
shelf_2_obj = self.env['sf.shelf'].search([('name', '=', '一号产线-二号线边刀架')], limit=1)
tool_location_objs_2 = self.env['sf.shelf.location'].search([('shelf_id', '=', shelf_2_obj.id)], order='id')
location_codes_2 = [location.barcode for location in tool_location_objs_2]
print(location_codes_2)
# 对方的数据列表
their_data_2 = [f"ToolCab2-{i:02}" for i in range(1, 73)]
# 执行对齐
aligned_data_2 = align_data(location_codes_2, their_data_2)
# 4
shelf_4_obj = self.env['sf.shelf'].search([('name', '=', '一号产线-一号线边料架')], limit=1)
tool_location_objs_4 = self.env['sf.shelf.location'].search([('shelf_id', '=', shelf_4_obj.id)], order='id')
location_codes_4 = [location.barcode for location in tool_location_objs_4]
print(location_codes_4)
# 对方的数据列表
their_data_4 = [f"PartCab4-{i:02}" for i in range(1, 17)]
# 执行对齐
aligned_data_4 = align_data(location_codes_4, their_data_4)
# 3
shelf_3_obj = self.env['sf.shelf'].search([('name', '=', '一号产线-二号线边料架')], limit=1)
tool_location_objs_3 = self.env['sf.shelf.location'].search([('shelf_id', '=', shelf_3_obj.id)], order='id')
location_codes_3 = [location.barcode for location in tool_location_objs_3]
print(location_codes_3)
# 对方的数据列表
their_data_3 = [f"PartCab3-{i:02}" for i in range(1, 13)]
# 执行对齐
aligned_data_3 = align_data(location_codes_3, their_data_3)
# 5
shelf_5_obj = self.env['sf.shelf'].search([('name', '=', '一号产线-三号线边料架')], limit=1)
tool_location_objs_5 = self.env['sf.shelf.location'].search([('shelf_id', '=', shelf_5_obj.id)], order='id')
location_codes_5 = [location.barcode for location in tool_location_objs_5]
print(location_codes_5)
# 对方的数据列表
their_data_5 = [f"PartCab5-{i:02}" for i in range(1, 13)]
# 执行对齐
aligned_data_5 = align_data(location_codes_5, their_data_5)
total_data = aligned_data_1 + aligned_data_2 + aligned_data_3 + aligned_data_4 + aligned_data_5
print(total_data)
logging.info(f"total_data: {total_data}")
def find_their_code(my_code, aligned_data): def find_their_code(my_code, aligned_data):
for code_pair in aligned_data: for code_pair in aligned_data:
if code_pair[0] == my_code: if code_pair[0] == my_code:
return code_pair[1] return code_pair[1]
return None # 如果没有找到对应的值返回None或适当的默认值 return None # 如果没有找到对应的值返回None或适当的默认值
def find_our_code(their_code, aligned_data):
for code_pair in aligned_data:
if code_pair[1] == their_code:
return code_pair[0]
return None # 如果没有找到对应的值返回None或适当的默认值
# 定时更新所有设备机床刀库信息 # 定时更新所有设备机床刀库信息
equipment_ids = self.env['maintenance.equipment'].search( equipment_ids = self.env['maintenance.equipment'].search(
[('equipment_type', '=', '机床'), ('function_type', '!=', False)]) [('equipment_type', '=', '机床'), ('function_type', '!=', False)])
@@ -107,11 +103,9 @@ class MrsShelfLocationDataSync(models.Model):
equipment_id.register_equipment_tool() equipment_id.register_equipment_tool()
shelfinfo = self.env['sf.shelf.location'].get_sf_shelf_location_info() shelfinfo = self.env['sf.shelf.location'].get_sf_shelf_location_info()
total_data = self.get_total_data()
print('shelfinfo:', shelfinfo) print('shelfinfo:', shelfinfo)
for item in shelfinfo: for item in shelfinfo:
logging.info('货架已获取信息:%s' % item) shelf_barcode = find_our_code(item['Postion'], total_data)
shelf_barcode = self.find_our_code(total_data, item['Postion'])
location_id = self.env['sf.shelf.location'].search([('barcode', '=', shelf_barcode)], limit=1) location_id = self.env['sf.shelf.location'].search([('barcode', '=', shelf_barcode)], limit=1)
if location_id: if location_id:
# 如果是线边刀库信息,则对功能刀具移动生成记录 # 如果是线边刀库信息,则对功能刀具移动生成记录
@@ -121,16 +115,8 @@ class MrsShelfLocationDataSync(models.Model):
tool.tool_in_out_stock_location(location_id) tool.tool_in_out_stock_location(location_id)
if tool: if tool:
location_id.product_sn_id = tool.barcode_id.id location_id.product_sn_id = tool.barcode_id.id
# 修改功能刀具状态
if item.get('State') == '报警':
if tool.functional_tool_status != item.get('State'):
tool.write({
'functional_tool_status': item['State']
})
else: else:
location_id.product_sn_id = False location_id.product_sn_id = False
if item['RfidCode']:
logging.info('Rfid为【%s】的功能刀具在系统中不存在!' % item['RfidCode'])
else: else:
stock_lot_obj = self.env['stock.lot'].search([('rfid', '=', item['RfidCode'])], limit=1) stock_lot_obj = self.env['stock.lot'].search([('rfid', '=', item['RfidCode'])], limit=1)
if stock_lot_obj: if stock_lot_obj:
@@ -138,6 +124,7 @@ class MrsShelfLocationDataSync(models.Model):
else: else:
location_id.product_sn_id = False location_id.product_sn_id = False
logging.info('货架已获取信息:%s' % item)
except Exception as e: except Exception as e:
logging.info("捕获错误信息:%s" % e) logging.info("捕获错误信息:%s" % e)
raise ValidationError("数据错误导致同步失败,请联系管理员") raise ValidationError("数据错误导致同步失败,请联系管理员")

View File

@@ -21,8 +21,7 @@
<!-- ]"/> --> <!-- ]"/> -->
<field name="destination_location_id" domain="[('location_id', '=', location_dest_id_value), '|', <field name="destination_location_id" domain="[('location_id', '=', location_dest_id_value), '|',
('location_status', '=', '空闲'), ('product_id', '=', current_product_id), ('product_sn_id', ('location_status', '=', '空闲'), ('product_id', '=', current_product_id), ('product_sn_id',
'=', there_is_no_sn), ('rotative_Boolean', '=', False)]" '=', there_is_no_sn)]" options="{'no_create': True,'no_create_edit':True}"/>
options="{'no_create': True,'no_create_edit':True}"/>
<field name="rfid_barcode" string="Rfid"/> <field name="rfid_barcode" string="Rfid"/>
<!-- <field name="location_dest_id_product_type"/> --> <!-- <field name="location_dest_id_product_type"/> -->