Compare commits

..

2 Commits

Author SHA1 Message Date
guanhuan
56f1ba0f25 用户新增企业微信id 2024-09-20 14:24:50 +08:00
guanhuan
d067c5b8c4 左侧图标修改 2024-09-20 09:49:13 +08:00
59 changed files with 258 additions and 483 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 533 B

View File

@@ -234,7 +234,7 @@
</record>
<menuitem name="系统工单" id="work_order_1_list" web_icon="jikimo_system_order,static/description/icon.png"/>
<menuitem name="系统工单" id="work_order_1_list" web_icon="jikimo_system_order,static/description/系统工单.png"/>
<menuitem name="工单" id="work_order" parent="work_order_1_list" action="system_order"/>
<menuitem name="工单模板" id="work_order_template" parent="work_order_1_list" action="work_template" groups="jikimo_system_order.group_operations_permissions_rwc"/>
<menuitem name="工单分类" id="work_order_type" parent="work_order_1_list" action="classify" groups="jikimo_system_order.group_operations_permissions_rwc"/>

Binary file not shown.

After

Width:  |  Height:  |  Size: 702 B

View File

@@ -1024,7 +1024,7 @@
<menuitem
id="menu_quality_root"
name="Quality"
web_icon="quality_control,static/description/icon.svg"
web_icon="quality_control,static/description/质量.png"
sequence="150"
groups="quality.group_quality_user"/>

View File

@@ -242,8 +242,3 @@ access_sf_fixture_materials_basic_parameters_group_sf_stock_manager,sf_fixture_m
access_sf_multi_mounting_type_group_sf_stock_manager,sf_multi_mounting_type_group_sf_stock_manager,model_sf_multi_mounting_type,sf_base.group_sf_stock_manager,1,0,0,0
access_sf_machine_brand_group_sf_stock_manager,sf_machine_brand_group_sf_stock_manager,model_sf_machine_brand,sf_base.group_sf_stock_manager,1,0,0,0
access_sf_cutting_tool_type_group_sf_stock_manager,sf_cutting_tool_type_group_sf_stock_manager,model_sf_cutting_tool_type,sf_base.group_sf_stock_manager,1,0,0,0
access_sf_cutting_tool_material_group_plan_dispatch,sf_cutting_tool_material_group_plan_dispatch,model_sf_cutting_tool_material,sf_base.group_plan_dispatch,1,0,0,0
access_sf_functional_cutting_tool_model_group_plan_dispatch,sf_functional_cutting_tool_model_group_plan_dispatch,model_sf_functional_cutting_tool_model,sf_base.group_plan_dispatch,1,0,0,0
access_sf_cutting_tool_type_group_plan_dispatch,sf_cutting_tool_type_group_plan_dispatch,model_sf_cutting_tool_type,sf_base.group_plan_dispatch,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
242
243
244

View File

@@ -1,4 +1,3 @@
import traceback
from datetime import datetime
import logging
import requests
@@ -54,14 +53,11 @@ class StatusChange(models.Model):
if not ret.get('error'):
logging.info('接口已经执行=============')
else:
traceback_error = traceback.format_exc()
logging.error("bfm订单状态同步失败:%s request info %s" % traceback_error)
logging.error('/api/get/state/get_order 请求失败{}'.format(ret))
raise UserError('工厂加工同步订单状态到bfm失败')
logging.error('工厂加工同步订单状态失败 {}'.format(ret))
raise UserError('工厂加工同步订单状态失败')
except UserError as e:
traceback_error = traceback.format_exc()
logging.error("工厂加工同步订单状态失败:%s " % traceback_error)
raise UserError(e)
logging.error('工厂加工同步订单状态失败 {}'.format(e))
raise UserError('工厂加工同步订单状态失败')
return res
def action_cancel(self):

View File

@@ -7,10 +7,11 @@
'sequence': 1,
'category': 'sf',
'website': 'https://www.sf.jikimo.com',
'depends': ['hr'],
'depends': ['base', 'hr'],
'data': [
'views/hr_employee.xml',
'views/res_config_settings_views.xml',
'views/res_users_view.xml',
'data/cron_data.xml',
],
'demo': [

View File

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

View File

@@ -20,7 +20,9 @@ class JkmPracticeEmployee(models.Model):
if result['employee_list']:
for employee_info in result['employee_list']:
if employee_info['work_email']:
self.sudo().search([('work_email', '=', employee_info['work_email'])]).write(
{'we_id': employee_info['we_id']})
hr_employee = self.sudo().search([('work_email', '=', employee_info['work_email'])])
hr_employee.write({'we_id': employee_info['we_id']})
if hr_employee.user_id:
hr_employee.user_id.write({'we_employee_id': employee_info['we_id']})
else:
logging.info('_employee_info_sync error:%s' % result['message'])

12
sf_hr/models/res_users.py Normal file
View File

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

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="view_users_account_form" model="ir.ui.view">
<field name="name">res.users.account.form</field>
<field name="model">res.users</field>
<field name="inherit_id" ref="base.view_users_form"/>
<field name="arch" type="xml">
<page name="preferences" position="after">
<page name="account" string="企业微信">
<group>
<field name="we_employee_id"/>
</group>
</page>
</page>
</field>
</record>
</data>
</odoo>

View File

@@ -24,7 +24,7 @@
</div>
<div>
<h2>获取检测报告服务配置</h2>
<div class="row mt16 o_settings_container" id="check_report_config">
<div class="row mt16 o_settings_container" id="jd_api">
<div class="col-12 col-lg-6 o_setting_box">
<div class="o_setting_left_pane"/>
<div class="o_setting_right_pane">
@@ -38,18 +38,6 @@
</div>
</div>
</xpath>
<xpath expr="//div[@id='check_report_config']/div" position="after">
<div class="col-12 col-lg-6 o_setting_box">
<div class="o_setting_left_pane">
<field name="is_get_detection_file"/>
</div>
<div class="o_setting_right_pane">
<div class="text-muted">
<label for="is_get_detection_file"/>
</div>
</div>
</div>
</xpath>
</field>
</record>
</data>

View File

@@ -38,7 +38,8 @@ class SfMaintenanceEquipment(models.Model):
crea_url = "/api/machine_tool/create"
# AGV运行日志
#AGV运行日志
agv_logs = fields.One2many('maintenance.equipment.agv.log', 'equipment_id', string='AGV运行日志')
# 1212修改后的字段
number_of_axles = fields.Selection(
@@ -116,6 +117,7 @@ class SfMaintenanceEquipment(models.Model):
# num = "%04d" % m
# return num
equipment_maintenance_standards_ids = fields.Many2many('equipment.maintenance.standards',
'sf_maintenance_equipment_ids', string='设备维保标准')
eq_maintenance_id = fields.Many2one('equipment.maintenance.standards', string='设备保养标准',
@@ -177,8 +179,7 @@ class SfMaintenanceEquipment(models.Model):
type_id = fields.Many2one('sf.machine_tool.type', '型号')
state = fields.Selection(
[("正常", "正常"), ("故障停机", "故障停机"), ("计划维保", "计划维保"), ("空闲", "空闲"),
("封存(报废)", "封存(报废)")],
[("正常", "正常"), ("故障停机", "故障停机"), ("计划维保", "计划维保"), ("空闲", "空闲"), ("封存(报废)", "封存(报废)")],
default='正常', string="机床状态")
run_time = fields.Char('总运行时长')
# 0606新增字段
@@ -327,7 +328,7 @@ class SfMaintenanceEquipment(models.Model):
item.tool_diameter_min = item.type_id.tool_diameter_min
item.machine_tool_category = item.type_id.machine_tool_category.id
item.brand_id = item.type_id.brand_id.id
# 新增修改字段
#新增修改字段
item.taper_type_id = item.type_id.taper_type_id.id
item.function_type = item.type_id.function_type
item.a_axis = item.type_id.a_axis
@@ -369,6 +370,7 @@ class SfMaintenanceEquipment(models.Model):
item.image_id = item.type_id.jg_image_id.ids
item.image_lq_id = item.type_id.lq_image_id.ids
# AGV小车设备参数
AGV_L = fields.Char('AGV尺寸(长)')
AGV_W = fields.Char('AGV尺寸(宽)')
@@ -459,6 +461,18 @@ class SfMaintenanceEquipment(models.Model):
original_value = fields.Char('原值')
incomplete_value = fields.Char('残值')
# 注册同步机床
def enroll_machine_tool(self):
sf_sync_config = self.env['res.config.settings'].get_values()
@@ -749,7 +763,7 @@ class SfMaintenanceEquipment(models.Model):
image_id = fields.Many2many('maintenance.equipment.image', 'equipment_id',
domain="[('type', '=', '加工能力')]")
image_lq_id = fields.Many2many('maintenance.equipment.image', 'equipment_lq_id', string='冷却方式',
domain="[('type', '=', '冷却方式')]")
domain="[('type', '=', '冷却方式')]")
class SfRobotAxisNum(models.Model):
@@ -763,5 +777,4 @@ class SfRobotAxisNum(models.Model):
weight = fields.Char('最大负载(kg)')
permissible_load_torque = fields.Char('允许负载扭矩(N-m)')
permissible_inertial_torque = fields.Char('允许惯性扭矩(kg-m²)')
equipment_id = fields.Many2one('maintenance.equipment', string='机器人',
domain="[('equipment_type', '=', '机器人')]")
equipment_id = fields.Many2one('maintenance.equipment', string='机器人', domain="[('equipment_type', '=', '机器人')]")

Binary file not shown.

After

Width:  |  Height:  |  Size: 858 B

View File

@@ -1230,5 +1230,9 @@
action="hr_equipment_action1"
sequence="0"/>
<menuitem
id="maintenance.menu_maintenance_title"
web_icon="sf_maintenance,static/description/维护.png"/>
</odoo>

View File

@@ -45,6 +45,7 @@
'sf_manufacturing/static/src/scss/kanban_change.scss',
'sf_manufacturing/static/src/xml/button_show_on_tree.xml',
'sf_manufacturing/static/src/js/workpiece_delivery_wizard_confirm.js',
'sf_manufacturing/static/src/js/custom_barcode_handlers.js',
]
},

View File

@@ -1,10 +1,10 @@
import logging
import requests
from odoo import models, fields, api, _
from odoo.exceptions import UserError
import logging
_logger = logging.getLogger(__name__)
@@ -54,7 +54,7 @@ class AgvScheduling(models.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 item in domain:
for index, item in enumerate(domain):
if isinstance(item, list):
if item[0] == 'delivery_workpieces':
new_domain.append('&')
@@ -63,7 +63,7 @@ class AgvScheduling(models.Model):
continue
new_domain.append(item)
return super(AgvScheduling, self).web_search_read(new_domain, fields, offset, limit, order, count_limit)
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):

View File

@@ -318,10 +318,8 @@ class MrpProduction(models.Model):
# cnc程序获取
def fetchCNC(self, production_names):
cnc = self.env['mrp.production'].search([('id', '=', self.id)])
quick_order = False
if cnc.product_id.default_code:
quick_order = self.env['quick.easy.order'].search(
[('name', '=', cnc.product_id.default_code.rsplit('-', 1)[0])])
quick_order = self.env['quick.easy.order'].search(
[('name', '=', cnc.product_id.default_code.rsplit('-', 1)[0])])
programme_way = False
if cnc.manual_quotation is True:
programme_way = 'manual operation'
@@ -806,10 +804,6 @@ class MrpProduction(models.Model):
backorders = backorders - productions_to_backorder
productions_not_to_backorder._post_inventory(cancel_backorder=True)
# if self.workorder_ids.filtered(lambda w: w.routing_type in ['表面工艺']):
# move_finish = self.env['stock.move'].search([('created_production_id', '=', self.id)])
# if move_finish:
# move_finish._action_assign()
productions_to_backorder._post_inventory(cancel_backorder=True)
# if completed products make other confirmed/partially_available moves available, assign them

View File

@@ -144,8 +144,6 @@ class ResMrpWorkOrder(models.Model):
# 是否绑定托盘
is_trayed = fields.Boolean(string='是否绑定托盘', default=False)
tag_type = fields.Selection([("重新加工", "重新加工")], string="标签", tracking=True)
@api.depends('name', 'production_id.name')
def _compute_surface_technics_picking_ids(self):
for workorder in self:
@@ -428,18 +426,17 @@ class ResMrpWorkOrder(models.Model):
logging.info('local_file_path:%s' % local_file_path)
remote_path = '/home/ftp/ftp_root/ThreeTest/XT/Before/' + local_filename
logging.info('remote_path:%s' % remote_path)
is_get_detection_file = self.env['ir.config_parameter'].sudo().get_param('is_get_detection_file')
if not is_get_detection_file:
paload_data = {
"filename": local_filename
}
if not ftp_resconfig['get_check_file_path']:
raise UserError('请先配置获取检测报告地址')
url = ftp_resconfig['get_check_file_path'] + '/get/check/report'
response = requests.post(url, json=paload_data)
logging.info('response:%s' % response.json())
if response.json().get('detail'):
raise UserError(response.json().get('detail'))
# if not ftp.file_exists(remote_path):
paload_data = {
"filename": local_filename
}
if not ftp_resconfig['get_check_file_path']:
raise UserError('请先配置获取检测报告地址')
url = ftp_resconfig['get_check_file_path'] + '/get/check/report'
response = requests.post(url, json=paload_data)
logging.info('response:%s' % response.json())
if response.json().get('detail'):
raise UserError(response.json().get('detail'))
if not ftp.file_exists(remote_path):
raise UserError(f"文件不存在: {remote_path}")
@@ -606,8 +603,6 @@ class ResMrpWorkOrder(models.Model):
print("(%.2f,%.2f)" % (x, y))
self.material_center_point = ("(%.2f,%.2f,%.2f)" % (x, y, z))
self.X_deviation_angle = jdz
logging.info("坯料中心点坐标:(%.2f,%.2f)" % (x, y))
logging.info("X轴偏差度数:%.2f" % jdz)
# 将补偿值写入CNC加工工单
workorder = self.env['mrp.workorder'].browse(self.ids)
work = workorder.production_id.workorder_ids
@@ -710,7 +705,6 @@ class ResMrpWorkOrder(models.Model):
'date_planned_finished': datetime.now() + timedelta(days=1),
'duration_expected': duration_expected,
'duration': 0,
'tag_type': '重新加工' if item is False else False,
'cnc_ids': False if route.routing_type != 'CNC加工' else self.env['sf.cnc.processing']._json_cnc_processing(
k, item),
'cmm_ids': False if route.routing_type != 'CNC加工' else self.env['sf.cmm.program']._json_cmm_program(k,
@@ -1031,20 +1025,16 @@ class ResMrpWorkOrder(models.Model):
# 查询工序最小的非完工、非返工的装夹预调工单
work_id = self.search(
[('production_id', '=', workorder.production_id.id),
('routing_type', '=', '装夹预调'),
('state', 'not in', ['rework', 'done', 'cancel'])],
limit=1,
order="sequence")
if work_id.routing_type == '装夹预调':
if workorder == work_id:
if workorder.production_id.reservation_state == 'assigned':
workorder.state = 'ready'
elif workorder.production_id.reservation_state != 'assigned':
workorder.state = 'waiting'
continue
elif (workorder.name == '装夹预调' and
workorder.state not in ['rework', 'done', 'cancel']):
if workorder.state != 'pending':
workorder.state = 'pending'
if workorder == work_id:
if workorder.production_id.reservation_state == 'assigned':
workorder.state = 'ready'
elif workorder.production_id.reservation_state != 'assigned':
workorder.state = 'waiting'
continue
if workorder.production_id.tool_state in ['1', '2'] and workorder.state == 'ready':
workorder.state = 'waiting'
continue
@@ -1189,10 +1179,8 @@ class ResMrpWorkOrder(models.Model):
if not record.rfid_code and record.is_rework is False:
raise UserError("请扫RFID码进行绑定")
if record.is_rework is False:
if not record.material_center_point:
raise UserError("坯料中心点为空,请检查")
if record.X_deviation_angle <= 0:
raise UserError("X偏差角度小于等于0请检查本次计算的X偏差角度为%s" % record.X_deviation_angle)
if not record.material_center_point or record.X_deviation_angle <= 0:
raise UserError("坯料中心点为空或X偏差角度小于等于0")
record.process_state = '待加工'
# record.write({'process_state': '待加工'})
record.production_id.process_state = '待加工'
@@ -1350,7 +1338,6 @@ class ResMrpWorkOrder(models.Model):
arch = etree.fromstring(tree_view['arch'])
# 查找 tree 标签
tree_element = arch.xpath("//tree")[0]
tree_element.set('js_class', 'remove_focus_list_view')
# 查找或创建 header 标签
header_element = tree_element.find('header')
@@ -1573,8 +1560,6 @@ class SfWorkOrderBarcodes(models.Model):
def on_barcode_scanned(self, barcode):
logging.info('Rfid:%s' % barcode)
if 'O-CMD' in barcode:
return None
workorder = self.env['mrp.workorder'].browse(self.ids)
# workorder_preset = self.env['mrp.workorder'].search(
# [('routing_type', '=', '装夹预调'), ('rfid_code', '=', barcode)])

View File

@@ -4,39 +4,19 @@ from odoo import models, fields, api
class ResConfigSettings(models.TransientModel):
_inherit = 'res.config.settings'
agv_rcs_url = fields.Char(string='avg_rcs访问地址',
default='http://172.16.10.114:8182/rcms/services/rest/hikRpcService/genAgvSchedulingTask')
wbcode = fields.Char('地码')
agv_code = fields.Char(string='agv编号')
task_type_no = fields.Char('任务单类型编号')
is_agv_task_dispatch = fields.Boolean('是否下发AGV任务', default=False)
# 是否重新获取检测文件
is_get_detection_file = fields.Boolean(string='重新获取检测文件', default=False)
@api.model
def get_values(self):
values = super(ResConfigSettings, self).get_values()
config = self.env['ir.config_parameter'].sudo()
agv_rcs_url = config.get_param('agv_rcs_url', default='')
wbcode = config.get_param('wbcode', default='')
agv_code = config.get_param('agv_code', default='')
is_agv_task_dispatch = config.get_param('is_agv_task_dispatch')
is_get_detection_file = config.get_param('is_get_detection_file')
values.update(
agv_rcs_url=agv_rcs_url,
wbcode=wbcode,
agv_code=agv_code,
is_agv_task_dispatch=is_agv_task_dispatch,
is_get_detection_file=is_get_detection_file
)
return values
def set_values(self):
super(ResConfigSettings, self).set_values()
config = self.env['ir.config_parameter'].sudo()
config.set_param("agv_rcs_url", self.agv_rcs_url or "")
config.set_param("wbcode", self.wbcode or "")
config.set_param("agv_code", self.agv_code or "")
config.set_param("is_agv_task_dispatch", self.is_agv_task_dispatch or False)
config.set_param("is_get_detection_file", self.is_get_detection_file or False)

View File

@@ -269,9 +269,8 @@ class StockRule(models.Model):
sale_order = self.env['sale.order'].sudo().search([('name', '=', production.origin)])
# 根据销售订单号查询快速订单
quick_easy_order = self.env['quick.easy.order'].sudo().search([('sale_order_id', '=', sale_order.id)])
if quick_easy_order:
production.write({'part_number': quick_easy_order.part_drawing_number,
'part_drawing': quick_easy_order.machining_drawings})
production.write({'part_number': quick_easy_order.part_drawing_number,
'part_drawing': quick_easy_order.machining_drawings})
if sale_order:
# sale_order.write({'schedule_status': 'to schedule'})
self.env['sf.production.plan'].sudo().with_company(company_id).create({

View File

@@ -32,8 +32,6 @@
</field>
<xpath expr="//field[@name='qty_remaining']" position="after">
<field name="manual_quotation" optional="show"/>
<field name='tag_type' widget="badge"
decoration-danger="tag_type == '重新加工'"/>
</xpath>
<xpath expr="//field[@name='date_planned_start']" position="replace">
<field name="date_planned_start" string="计划开始日期" optional="show"/>
@@ -45,11 +43,11 @@
<field name="date_planned_finished" string="计划结束日期" optional="hide"/>
</xpath>
<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',-->
<!-- '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',
'done',
'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel')),
@@ -167,8 +165,8 @@
<!-- 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="是否确认完工"/> -->
<!-- <button name="button_start" type="object" string="开始" class="btn-success" confirm="是否确认开始"-->
<!-- attrs="{'invisible': ['|', '|', '|', ('production_state','in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel','to be detected')), ('is_user_working', '!=', False)]}"/>-->
<!-- <button name="button_start" type="object" string="开始" class="btn-success" confirm="是否确认开始"-->
<!-- attrs="{'invisible': ['|', '|', '|', ('production_state','in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel','to be detected')), ('is_user_working', '!=', False)]}"/>-->
<button name="button_start" type="object" string="开始" class="btn-success" confirm="是否确认开始"
attrs="{'invisible': ['|', '|', '|', '|', '|', ('routing_type', '=', '装夹预调'), ('routing_type', '=', '解除装夹'), ('production_state','in', ('draft', 'done', 'cancel')), ('working_state', '=', 'blocked'), ('state', 'in', ('done', 'cancel','to be detected')), ('is_user_working', '!=', False)]}"/>
<button name="button_start" type="object" string="开始" class="btn-success"
@@ -193,8 +191,8 @@
<!-- context="{'default_workcenter_id': workcenter_id}" class="btn-danger" -->
<!-- groups="sf_base.group_sf_mrp_user" -->
<!-- attrs="{'invisible': ['|', '|', ('production_state', 'in', ('draft', 'done', 'cancel')), ('working_state', '!=', 'blocked'),('state','=','done')]}"/> -->
<!-- <button name="button_workpiece_delivery" type="object" string="工件配送" class="btn-primary"-->
<!-- attrs="{'invisible': ['|','|','|','|',('routing_type','!=','装夹预调'),('is_delivery','=',True),('state','!=','done'),('is_rework','=',True),'&amp;',('rfid_code','in',['',False]),('state','=','done')]}"/>-->
<!-- <button name="button_workpiece_delivery" type="object" string="工件配送" class="btn-primary"-->
<!-- attrs="{'invisible': ['|','|','|','|',('routing_type','!=','装夹预调'),('is_delivery','=',True),('state','!=','done'),('is_rework','=',True),'&amp;',('rfid_code','in',['',False]),('state','=','done')]}"/>-->
<button name="button_rework_pre" type="object" string="返工"
class="btn-primary"
attrs="{'invisible': ['|','|',('routing_type','!=','装夹预调'),('state','!=','progress'),('is_rework','=',True)]}"/>
@@ -223,12 +221,9 @@
<xpath expr="//label[1]" position="before">
<field name='routing_type' readonly="1"/>
<field name='process_state' attrs='{"invisible": [("routing_type","!=","装夹预调")]}'/>
<field name='tag_type' readonly="1" attrs='{"invisible": [("tag_type","=",False)]}'
decoration-danger="tag_type == '重新加工'"/>
<field name="rfid_code" force_save="1" readonly="1" cache="True"
attrs="{'invisible': [('rfid_code_old', '!=', False)]}"/>
<field name="rfid_code_old" readonly="1" attrs="{'invisible': [('rfid_code_old', '=', False)]}"/>
</xpath>
<xpath expr="//label[1]" position="attributes">
<attribute name="string">计划加工时间</attribute>
@@ -484,10 +479,10 @@
<div class="col-12 col-lg-6 o_setting_box">
<field name="data_state" invisible="1"/>
<!-- <button type="object" class="oe_highlight" name="get_three_check_datas" string="获取数据" -->
<!-- attrs='{"invisible": ["|", "|", "|", ("material_center_point","!=",False),("state","!=","progress"),("user_permissions","=",False), ("data_state", "=", True)]}'/> -->
<!-- <button type="object" class="oe_highlight" name="getcenter" string="计算定位" -->
<!-- attrs='{"invisible": ["|","|", "|",("material_center_point","!=",False),("state","!=","progress"),("user_permissions","=",False), ("data_state", "=", False)]}'/> -->
<button type="object" class="oe_highlight" name="get_three_check_datas" string="获取数据"
attrs='{"invisible": ["|", "|", "|", ("material_center_point","!=",False),("state","!=","progress"),("user_permissions","=",False), ("data_state", "=", True)]}'/>
<button type="object" class="oe_highlight" name="getcenter" string="计算定位"
attrs='{"invisible": ["|","|", "|",("material_center_point","!=",False),("state","!=","progress"),("user_permissions","=",False), ("data_state", "=", False)]}'/>
</div>
<group>
@@ -519,8 +514,8 @@
</xpath>
<xpath expr="//form//header" position="inside">
<button type="object" class="oe_highlight jikimo_button_confirm" name="get_three_check_datas"
string="获取数据" attrs='{"invisible": [("state","!=","progress")]}'/>
<button type="object" class="oe_highlight" name="get_three_check_datas" string="获取数据"
attrs='{"invisible": [("state","!=","progress")]}'/>
</xpath>
@@ -671,8 +666,7 @@
<field name="arch" type="xml">
<tree string="工件配送" class="center" create="0" delete="0" js_class="remove_focus_list_view">
<header>
<button name="button_delivery" type="object" string="工件配送"
class="btn-primary jikimo_button_confirm" attrs="{'force_show':1}"/>
<button name="button_delivery" type="object" string="工件配送" class="btn-primary jikimo_button_confirm" attrs="{'force_show':1}"/>
</header>
<field name="status" widget="badge"
decoration-success="status == '已配送'"
@@ -684,15 +678,15 @@
<field name="production_id"/>
<field name="type" readonly="1"/>
<field name="production_line_id" options="{'no_create': True}" readonly="1"/>
<!-- <field name="route_id" options="{'no_create': True}"-->
<!-- domain="[('route_type','in',['上产线','下产线'])]"/>-->
<!-- <field name="route_id" options="{'no_create': True}"-->
<!-- domain="[('route_type','in',['上产线','下产线'])]"/>-->
<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="rfid_code"/>-->
<!-- <field name="task_delivery_time" readonly="1"/>-->
<!-- <field name="task_completion_time" readonly="1"/>-->
<!-- <field name="delivery_duration" widget="float_time"/>-->
<!-- <field name="task_delivery_time" readonly="1"/>-->
<!-- <field name="task_completion_time" readonly="1"/>-->
<!-- <field name="delivery_duration" widget="float_time"/>-->
</tree>
</field>
</record>
@@ -859,10 +853,10 @@
<field name="domain">[('type','in',['运送空料架']),('name','not ilike','WDO')]</field>
</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"/>
<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>

View File

@@ -6,32 +6,6 @@
<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[hasclass('app_settings_block')]/div" position="before">
<div>
<h2>AGV参数配置</h2>
<div class="row mt16 o_settings_container" id="agv_config">
<div class="col-12 col-lg-6 o_setting_box">
<div class="o_setting_left_pane"/>
<div class="o_setting_right_pane">
<div class="text-muted">
<label for="agv_rcs_url" string="访问地址"/>
<field name="agv_rcs_url"/>
</div>
<div class="text-muted">
<label for="agv_code" string="车辆编号"/>
<field name="agv_code"/>
</div>
<div class="text-muted">
<label for="wbcode"/>
<field name="wbcode"/>
</div>
</div>
</div>
</div>
</div>
</xpath>
<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">

View File

@@ -14,7 +14,7 @@
<group col="1">
<field name="production_ids" readonly="1" widget="many2many_tags" string="制造订单号"/>
<field name="delivery_type" readonly="1"/>
<field name="feeder_station_start_id" string="当前接驳站" options="{'no_create': True}" required="1"/>
<field name="feeder_station_start_id" options="{'no_create': True}" required="1"/>
<field name="workcenter_id" options="{'no_create': True}"/>
</group>
<footer>

View File

@@ -179,15 +179,18 @@ class WorkpieceDeliveryWizard(models.TransientModel):
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')
# 判断barcode是否是数字
if not barcode.isdigit():
# 判断是否是AGV接驳站名称
agv_site = self.env['sf.agv.site'].search([('name', '=', barcode)])
if agv_site:
self.feeder_station_start_id = agv_site.id # 修正:移除 .id
if not self.feeder_station_start_id:
self.feeder_station_start_id = agv_site.id
else:
if self.feeder_station_start_id.id != agv_site.id:
raise UserError('起点接驳站不匹配!')
return
delivery_type = self.env.context.get('default_delivery_type')
if delivery_type == '上产线':
workorder = self.env['mrp.workorder'].search(
[('production_line_state', '=', '待上产线'), ('rfid_code', '=', barcode),
@@ -207,14 +210,11 @@ class WorkpieceDeliveryWizard(models.TransientModel):
workorder.production_line_id.id != self.production_ids[0].production_line_id.id):
raise UserError(f'该rfid对应的制造订单号为{workorder.production_id.name}的目的生产线不一致')
# 调用打印成品条码方法
workorder.print_method()
# 将对象添加到对应的同模型且是多对多类型里
self.production_ids |= workorder.production_id
self.workorder_ids |= workorder
down_product_agv_scheduling = workorder.get_down_product_agv_scheduling()
down_product_agv_scheduling = self.get_down_product_agv_scheduling()
if down_product_agv_scheduling:
if not self.feeder_station_start_id:
self.feeder_station_start_id = down_product_agv_scheduling.end_site_id.id
@@ -226,11 +226,4 @@ class WorkpieceDeliveryWizard(models.TransientModel):
raise UserError('该rfid码对应的工单不存在')
return
@api.onchange('feeder_station_start_id')
def on_start_id_change(self):
if self.delivery_type == '运送空料架' and len(self.workorder_ids) > 0:
down_product_agv_scheduling = self.workorder_ids[0].get_down_product_agv_scheduling()
if down_product_agv_scheduling and self.feeder_station_start_id \
and down_product_agv_scheduling.end_site_id.id != self.feeder_station_start_id.id:
raise UserError('当前接驳站不匹配!')

View File

@@ -11,7 +11,7 @@
""",
'category': 'sf',
'website': 'https://www.sf.jikimo.com',
'depends': ['base', 'sf_plan', 'sf_sale'],
'depends': ['base', 'sf_base'],
'data': [
'security/ir.model.access.csv',
'views/sf_message_template_view.xml',

View File

@@ -1,8 +1 @@
from . import sf_message_template
from . import sf_message_sale
from . import sf_message_plan
from . import sf_message_stock_picking
from . import sf_message_cam_program
from . import sf_message_functional_tool_assembly
from . import sf_message_purchase
from . import sf_message_workorder

View File

@@ -1,6 +0,0 @@
from odoo import models, fields, api, _
class SFMessageCamProgram(models.Model):
_name = 'sf.cam.work.order.program.knife.plan'
_inherit = ['sf.cam.work.order.program.knife.plan', 'sf.message.template']

View File

@@ -1,6 +0,0 @@
from odoo import models, fields, api, _
class SFMessagefunctionalToolAssembly(models.Model):
_name = 'sf.functional.tool.assembly'
_inherit = ['sf.functional.tool.assembly', 'sf.message.template']

View File

@@ -1,6 +0,0 @@
from odoo import models, fields, api, _
class SFMessagePlan(models.Model):
_name = 'sf.production.plan'
_inherit = ['sf.production.plan', 'sf.message.template']

View File

@@ -1,6 +0,0 @@
from odoo import models, fields, api, _
class SFMessagePurchase(models.Model):
_name = 'purchase.order'
_inherit = ['purchase.order', 'sf.message.template']

View File

@@ -1,11 +0,0 @@
from odoo import models, fields, api, _
class SFMessageSale(models.Model):
_name = 'sale.order'
_inherit = ['sale.order', 'sf.message.template']
# def create(self):
# res = super(SFMessageSale, self).create()
# if res is True:

View File

@@ -1,6 +0,0 @@
from odoo import models, fields, api, _
class SFMessageStockPicking(models.Model):
_name = 'stock.picking'
_inherit = ['stock.picking', 'sf.message.template']

View File

@@ -1,6 +1,5 @@
# -*- coding: utf-8 -*-
from odoo import models, fields, api
from abc import ABC, abstractmethod
class SfMessageTemplate(models.Model):
@@ -8,6 +7,30 @@ class SfMessageTemplate(models.Model):
_description = u'消息模板'
name = fields.Char(string=u"名称", required=True)
type = fields.Selection([
('待接单', '待接单'),
('待排程', '待排程'),
('坯料采购', '坯料采购'),
('坯料发料', '坯料发料'),
('待编程', '待编程'),
('调拨入库', '调拨入库'),
('功能刀具组装', '功能刀具组装'),
('功能刀具寿命到期', '功能刀具寿命到期'),
('程序用刀计划异常', '程序用刀计划异常'),
('工单无CNC程序', '工单无CNC程序'),
('生产线无功能刀具', '生产线无功能刀具'),
('工单无定位数据', '工单无定位数据'),
('工单FTP无文件', '工单FTP无文件'),
('工单加工失败', '工单加工失败'),
('设备故障及异常', '设备故障及异常'),
('工单逾期预警', '工单逾期预警'),
('工单已逾期', '工单已逾期'),
('销售订单逾期', '销售订单逾期'),
('销售订单已逾期', '销售订单已逾期'),
('待质量判定', '待质量判定'),
('生产完工待入库', '生产完工待入库'),
('订单发货', '订单发货')
], string='类型', required=True)
description = fields.Char(string=u"描述")
content = fields.Html(string=u"内容", render_engine='qweb', translate=True, prefetch=True, sanitize=False)
msgtype = fields.Selection(
@@ -17,18 +40,9 @@ class SfMessageTemplate(models.Model):
notification_employee_ids = fields.Many2many('hr.employee', string=u'员工',
domain="[('department_id', '=',notification_department_id)]",
required=True)
is_send_time = fields.Boolean(string=u"定时发送", default=False)
send_time_1 = fields.Integer('发送时间点1')
send_time_2 = fields.Integer('发送时间点2')
active = fields.Boolean(string=u"是否有效", default=True)
@api.onchange('notification_department_id')
def _clear_employee_ids(self):
if self.notification_department_id:
self.notification_employee_ids = False
@abstractmethod
def dispatch(self, args):
"""
强迫继承该类必走该抽象方法'
"""

View File

@@ -1,6 +0,0 @@
from odoo import models, fields, api, _
class SFMessageWork(models.Model):
_name = 'mrp.workorder'
_inherit = ['mrp.workorder', 'sf.message.template']

View File

@@ -18,14 +18,12 @@
</h1>
</div>
<group>
<!-- <field name="type"/>-->
<field name="type"/>
<field name="content" widget="html" class="oe-bordered-editor"
options="{'style-inline': true, 'codeview': true, 'dynamic_placeholder': true}"/>
<field name="description"/>
<field name="msgtype"/>
<field name="is_send_time"/>
<field name="send_time_1" attrs="{'invisible': [('is_send_time', '=', False)]}"/>
<field name="send_time_2" attrs="{'invisible': [('is_send_time', '=', False)]}"/>
<field name="type"/>
<field name="notification_department_id"/>
<field name="notification_employee_ids" widget="many2many_tags"/>
</group>
@@ -40,12 +38,10 @@
<field name="arch" type="xml">
<tree string="消息模板">
<field name="name"/>
<!-- <field name="type"/>-->
<field name="type"/>
<field name="content"/>
<field name="msgtype"/>
<field name="is_send_time"/>
<field name="send_time_1" attrs="{'invisible': [('is_send_time', '=', False)]}"/>
<field name="send_time_2" attrs="{'invisible': [('is_send_time', '=', False)]}"/>
<field name="type"/>
<field name="notification_department_id"/>
<field name="notification_employee_ids" widget="many2many_tags"/>
<field name="description"/>
@@ -59,7 +55,7 @@
<field name="arch" type="xml">
<search>
<field name="name" string="模糊搜索"
filter_domain="['|','|',('name','like',self),('description','like',self)]"/>
filter_domain="['|','|',('name','like',self),('type','like',self),('description','like',self)]"/>
<field name="name"/>
<filter name="filter_active" string="已归档" domain="[('active','=',False)]"/>
</search>

View File

@@ -10,12 +10,11 @@
""",
'category': 'sf',
'website': 'https://www.sf.cs.jikimo.com',
'depends': ['sf_base', 'base_setup','sf_bf_connect'],
'depends': ['sf_base', 'base_setup'],
'data': [
'data/ir_cron_data.xml',
'security/ir.model.access.csv',
'views/res_config_settings_views.xml',
'views/order_price.xml',
'views/res_config_settings_views.xml'
],
'demo': [
],

View File

@@ -1,4 +1,3 @@
from . import ftp_operate
from . import res_config_setting
from . import sync_common
from . import order_price

View File

@@ -1,29 +0,0 @@
from odoo import fields, models, api
class OrderPrice(models.Model):
_name = 'order.price'
_description = '订单价格对比'
sale_order_id = fields.Many2one('sale.order', '销售订单')
bfm_order_name = fields.Char(related="sale_order_id.default_code", string='bfm订单号')
sale_order_name = fields.Char(related="sale_order_id.name", string='销售订单号')
currency_id = fields.Many2one(
related='sale_order_id.currency_id', string='货币', store=True)
sale_order_amount_total = fields.Monetary(related="sale_order_id.amount_total", tracking=4, string='销售订单金额')
bfm_amount_total = fields.Float(string='价格合计', compute='_compute_bfm_amount_total', store=True)
def is_float(self,value):
try:
float(value)
return True
except ValueError:
return False
@api.depends('sale_order_id.remark')
def _compute_bfm_amount_total(self):
for record in self:
amount_total = 0
for line in record.sale_order_id.order_line:
# 判断remark是否存在并且是否是数字
if line.remark and self.is_float(line.remark):
amount_total += float(line.remark)
record.bfm_amount_total = amount_total

View File

@@ -16,10 +16,13 @@ class ResConfigSettings(models.TransientModel):
token = fields.Char(string='TOKEN', default='b811ac06-3f00-11ed-9aed-0242ac110003')
sf_secret_key = fields.Char(string='密钥', default='wBmxej38OkErKhD6')
sf_url = fields.Char(string='访问地址', default='https://sf.cs.jikimo.com')
agv_rcs_url = fields.Char(string='avg_rcs访问地址',
default='http://172.16.10.114:8182/rcms/services/rest/hikRpcService/genAgvSchedulingTask')
center_control_url = fields.Char(string='中控访问地址',
default='http://172.16.21.50:8001')
center_control_Authorization = fields.Char(string='中控访问认证')
wbcode = fields.Char('地码')
agv_code = fields.Char(string='agv编号')
task_type_no = fields.Char('任务单类型编号')
model_parser_url = fields.Char('特征识别路径')
ftp_host = fields.Char(string='FTP的ip')
@@ -100,7 +103,9 @@ class ResConfigSettings(models.TransientModel):
token = config.get_param('token', default='')
sf_secret_key = config.get_param('sf_secret_key', default='')
sf_url = config.get_param('sf_url', default='')
agv_rcs_url = config.get_param('agv_rcs_url', default='')
wbcode = config.get_param('wbcode', default='')
agv_code = config.get_param('agv_code', default='')
center_control_url = config.get_param('center_control_url', default='')
center_control_Authorization = config.get_param('center_control_Authorization', default='')
ftp_host = config.get_param('ftp_host', default='')
@@ -113,7 +118,9 @@ class ResConfigSettings(models.TransientModel):
token=token,
sf_secret_key=sf_secret_key,
sf_url=sf_url,
agv_rcs_url=agv_rcs_url,
wbcode=wbcode,
agv_code=agv_code,
center_control_url=center_control_url,
center_control_Authorization=center_control_Authorization,
ftp_host=ftp_host,
@@ -130,7 +137,9 @@ class ResConfigSettings(models.TransientModel):
ir_config.set_param("token", self.token or "")
ir_config.set_param("sf_secret_key", self.sf_secret_key or "")
ir_config.set_param("sf_url", self.sf_url or "")
ir_config.set_param("agv_rcs_url", self.agv_rcs_url or "")
ir_config.set_param("wbcode", self.wbcode or "")
ir_config.set_param("agv_code", self.agv_code or "")
ir_config.set_param("center_control_url", self.center_control_url or "")
ir_config.set_param("center_control_Authorization", self.center_control_Authorization or "")
ir_config.set_param("ftp_host", self.ftp_host or "")
@@ -175,10 +184,7 @@ class ResConfigSettings(models.TransientModel):
new_price = res_order_lines_map.get(str(index))
if order_line:
# 修改单价
order_line.write({'remark': new_price*order_line.product_uom_qty})
order_price = self.env['order.price'].sudo().search([('sale_order_id', '=',need_change_sale_order.id )])
if not order_price:
self.env['order.price'].sudo().create({'sale_order_id':need_change_sale_order.id})
order_line.write({'remark': new_price})
else:
logging.error('同步销售订单价格失败 {}'.format(response.text))
raise UserError('同步销售订单价格失败')

View File

@@ -1,7 +1,7 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_sf_static_resource_datasync,sf_static_resource_datasync,model_sf_static_resource_datasync,base.group_user,1,1,1,1
access_order_price,order.price,model_order_price,base.group_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_sf_static_resource_datasync sf_static_resource_datasync model_sf_static_resource_datasync base.group_user 1 1 1 1
3
4
5
6
7

View File

@@ -1,27 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record model="ir.actions.act_window" id="order_price_tree_act">
<field name="name">bfm订单价格对比</field>
<field name="res_model">order.price</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem sequence="22" name="销售订单bfm对比" id="menu_sale_order_bfm_price"
action="order_price_tree_act"
parent="sale.sale_order_menu"
groups="base.group_user"
/>
<record id="view_order_price_tree" model="ir.ui.view">
<field name="name">order.price.list</field>
<field name="model">order.price</field>
<field name="arch" type="xml">
<tree string="订单计划">
<field name="bfm_order_name"/>
<field name="sale_order_name"/>
<field name="sale_order_amount_total"/>
<field name="bfm_amount_total"/>
</tree>
</field>
</record>
</odoo>

View File

@@ -74,7 +74,28 @@
</div>
</div>
</div>
<div>
<h2>AGV参数配置</h2>
<div class="row mt16 o_settings_container" id="agv_config">
<div class="col-12 col-lg-6 o_setting_box">
<div class="o_setting_left_pane"/>
<div class="o_setting_right_pane">
<div class="text-muted">
<label for="agv_rcs_url" string="访问地址"/>
<field name="agv_rcs_url"/>
</div>
<div class="text-muted">
<label for="agv_code" string="车辆编号"/>
<field name="agv_code"/>
</div>
<div class="text-muted">
<label for="wbcode"/>
<field name="wbcode"/>
</div>
</div>
</div>
</div>
</div>
<div>
<h2>中控参数配置</h2>
<div class="row mt16 o_settings_container">

View File

@@ -70,32 +70,6 @@ class sf_production_plan(models.Model):
sequence = fields.Integer(string='序号', copy=False, readonly=True, index=True)
current_operation_name = fields.Char(string='当前工序名称', size=64, default='生产计划')
@api.onchange('date_planned_start')
def date_planned_start_onchange(self):
if self.date_planned_start:
self.date_planned_finished = self.date_planned_start + timedelta(hours=1)
#处理计划状态非待排程,计划结束时间为空的数据处理
def deal_no_date_planned_finished(self):
plans = self.env['sf.production.plan'].search(
[('date_planned_finished', '=', False), ('state', 'in', ['processing', 'done', 'finished'])])
for item in plans:
if item.date_planned_start:
item.date_planned_finished = item.date_planned_start + timedelta(hours=1)
# 处理计划订单截止时间为空的数据
def deal_no_order_deadline(self):
plans = self.env['sf.production.plan'].sudo().search(
[('order_deadline', '=', False)])
for item in plans:
if item.date_planned_start:
item.order_deadline = item.date_planned_start + timedelta(days=7)
@api.model
def search_read(self, domain=None, fields=None, offset=0, limit=None, order=None):
info = super(sf_production_plan, self).search_read(domain, fields, offset, limit, order)
return info
# 计算实际加工时长
@api.depends('actual_start_time', 'actual_end_time')
def _compute_actual_process_time(self):
@@ -218,7 +192,7 @@ class sf_production_plan(models.Model):
return num
def do_production_schedule(self):
def do_production_schedule(self, date_planned_start):
"""
排程方法
"""
@@ -226,7 +200,8 @@ class sf_production_plan(models.Model):
if not record.production_line_id:
raise ValidationError("未选择生产线")
else:
is_schedule = self.deal_processing_schedule(record.date_planned_start)
is_schedule = self.deal_processing_schedule(date_planned_start)
if not is_schedule:
raise ValidationError("排程失败")
workorder_id_list = record.production_id.workorder_ids.ids
@@ -235,6 +210,7 @@ class sf_production_plan(models.Model):
for item in record.production_id.workorder_ids:
if item.name == 'CNC加工':
item.date_planned_finished = datetime.now() + timedelta(days=100)
# item.date_planned_start = record.date_planned_start
item.date_planned_start = self.date_planned_start if self.date_planned_start else datetime.now()
record.sudo().production_id.plan_start_processing_time = item.date_planned_start
item.date_planned_finished = item.date_planned_start + timedelta(
@@ -247,8 +223,6 @@ class sf_production_plan(models.Model):
record.date_planned_start, record.date_planned_finished = \
item.date_planned_start, item.date_planned_finished
record.state = 'done'
record.date_planned_finished = record.date_planned_start + timedelta(
minutes=60) if not record.date_planned_finished else record.date_planned_finished
# record.production_id.schedule_state = '已排'
record.sudo().production_id.schedule_state = '已排'
record.sudo().production_id.process_state = '待装夹'

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B

View File

@@ -88,8 +88,8 @@
<group string="加工信息">
<field name="date_planned_start" placeholder="如果不选择计划开始时间,会取当前时间来做排程" required="1"/>
<field name="date_planned_finished" required="1"/>
<field name="date_planned_start" placeholder="如果不选择计划开始时间,会取当前时间来做排程"/>
<field name="date_planned_finished"/>
<field name="actual_process_time"/>
<field name="actual_start_time"/>
<field name="actual_end_time"/>
@@ -278,6 +278,7 @@
sequence="150"
action="sf_production_plan_action"
groups="sf_base.group_plan_dispatch"
web_icon="sf_plan,static/description/计划.png"
/>
<!-- <record model="ir.ui.menu" id="mrp_custom_menu" inherit_id="mrp.menu_mrp_manufacturing"> -->

View File

@@ -6,7 +6,6 @@ from datetime import datetime
from odoo import fields, models
# from odoo.exceptions import ValidationError
from odoo.exceptions import UserError
from datetime import datetime, timedelta
_logger = logging.getLogger(__name__)
@@ -15,15 +14,10 @@ class Action_Plan_All_Wizard(models.TransientModel):
_name = 'sf.action.plan.all.wizard'
_description = u'排程向导'
def _get_date_planned_start(self):
planned_start_date = datetime.now() + timedelta(minutes=10)
logging.info('计划开始时间: %s', planned_start_date)
return planned_start_date
# 选择生产线
production_line_id = fields.Many2one('sf.production.line', string=u'生产线', required=True)
date_planned_start = fields.Datetime(string='计划开始时间', index=True, copy=False,
default=_get_date_planned_start)
default=fields.Datetime.now)
# 接收传递过来的计划ID
plan_ids = fields.Many2many('sf.production.plan', string=u'计划ID')
@@ -42,7 +36,7 @@ class Action_Plan_All_Wizard(models.TransientModel):
plan_obj = self.env['sf.production.plan'].browse(plan.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(self.date_planned_start)
# plan_obj.state = 'done'
print('处理计划:', plan.id, '完成')

View File

@@ -58,15 +58,6 @@ class QuickEasyOrder(models.Model):
part_drawing_number = fields.Char('零件图号')
machining_drawings = fields.Binary('2D加工图纸')
machining_drawings_name = fields.Char('2D加工图纸名')
@api.onchange('machining_drawings_name')
def _onchange_machining_drawings_name(self):
for item in self:
if item.machining_drawings_name:
if not item.machining_drawings_name.lower().endswith(
'.pdf'):
raise ValidationError('文件格式上传有误,请检查文件后缀(不区分大小写)是否为pdf')
@api.onchange('parameter_ids')
def _compute_parameter_ids(self):
@@ -137,10 +128,6 @@ class QuickEasyOrder(models.Model):
if len(item.upload_model_file) > 1:
raise ValidationError('只允许上传一个文件')
if item.upload_model_file:
if not item.upload_model_file.name.lower().endswith(
'.step') and not item.upload_model_file.name.lower().endswith(
'.stp'):
raise ValidationError('文件格式上传有误,请检查文件后缀(不区分大小写)是否为step、stp')
file_attachment_id = item.upload_model_file[0]
# 附件路径
report_path = file_attachment_id._full_path(file_attachment_id.store_fname)

View File

@@ -80,8 +80,7 @@
<field name="unit_price"/>
<field name="price" options="{'format': false}"/>
<field name="part_drawing_number"/>
<field name="machining_drawings" filename="machining_drawings_name" widget="pdf_viewer"/>
<field name="machining_drawings_name" invisible="1"/>
<field name="machining_drawings" widget="pdf_viewer"/>
<field name="sale_order_id"
attrs='{"invisible": [("sale_order_id","=",False)],"readonly": [("sale_order_id","!=",False)]}'/>
</group>

View File

@@ -3,4 +3,4 @@ from odoo import models, fields
class SyncFunctionalCuttingToolModel(models.Model):
_inherit = 'sf.functional.cutting.tool.model'
cutting_tool_type_ids = fields.Many2many('sf.cutting.tool.type', string='适用刀具物料类型')
cutting_tool_type_ids = fields.Many2many('sf.cutting.tool.type', string='适用刀具物料类型', required=True)

View File

@@ -30,27 +30,13 @@ class jikimo_bom(models.Model):
return result
def check_types_in_list(self):
"""
检查产品列表中的元素是否包含了所有指定的类型,并且每种类型至少出现一次。
:return: 如果条件满足返回True否则返回False
"""
if not self.product_ids:
return False
try:
# 统计每个类型的出现次数
type_counts = Counter(item.cutting_tool_material_id.name for item in self.product_ids)
# 检查是否每种类型的出现次数都大于0并且类型的数量与选项字符串中的数量相等
return all(count > 0 for count in type_counts.values()) and len(type_counts) == len(self.options.split('+'))
except AttributeError:
# 如果出现属性错误,说明产品列表中的元素可能缺少必要的属性
return False
# type_counts = Counter(item.cutting_tool_material_id.name for item in self.product_ids)
# return all(count > 0 for count in type_counts.values()) and len(type_counts) == self.options.split('+')
# 统计每个元素的类型
type_counts = Counter(item.cutting_tool_material_id.name for item in self.product_ids)
return all(count > 0 for count in type_counts.values()) and len(type_counts) == self.options.split('+')
def write(self, vals):
# 在更新模型时记录旧的 Many2many ID 列表
if 'product_ids' in vals and not self.env.context.get('is_assembly_options'):
if 'product_ids' in vals:
old_product_counter = Counter(self.product_ids.ids)
super(jikimo_bom, self).write(vals)
new_product_counter = Counter(self.product_ids.ids)
@@ -61,15 +47,9 @@ class jikimo_bom(models.Model):
return True
else:
raise UserError('每种物料最少要有一个')
return True
return super(jikimo_bom, self).write(vals)
def bom_product_domains(self, assembly_options):
"""
根据装配选项生成产品域列表
:param assembly_options: 装配选项字符串,各选项以'+'分隔
:return: 动态生成的产品搜索条件
"""
self.options = assembly_options
cutting_tool_materials = self.env['sf.cutting.tool.material'].search(
[('name', 'in', assembly_options.split('+'))])
@@ -102,25 +82,23 @@ class jikimo_bom(models.Model):
domains = domains + domain
if index != 0:
domains = ['|'] + domains
domains = domains + [('stock_move_ids', '!=',False)]
# wqwqwe = self.env['product.product'].search(ddd)
# product = self.env['product.product'].search(domain)
# if product:
# products = products + product
domains = domains + [('stock_move_count', '>', 0)]
return domains
def generate_bill_materials(self, assembly_options):
"""
生成物料清单
根据装配选项生成物料清单首先获取产品领域然后搜索相关产品并设置产品ID。
:param assembly_options: 组装方式
:type assembly_options: 装配选项字符串,各选项以'+'分隔
"""
domains = self.bom_product_domains(assembly_options)
products = self.env['product.product'].search(domains)
if products:
new_context = dict(self.env.context)
new_context['is_assembly_options'] = True
self.with_context(new_context).write({'product_ids': [Command.set(products.ids)]})
# self.product_ids = [Command.set(products.ids)]
self.product_ids = [Command.set(products.ids)]
# if option.name == '刀盘':
# hilt = self.env['product.product'].search(
# [('cutting_tool_blade_diameter', '=', self.tool_inventory_id.diameter),
# ('cutting_tool_material_id', '=', option.id)])
# self.product_ids = [Command.set(hilt.ids)]k
class jikimo_bom_line(models.Model):
@@ -133,15 +111,15 @@ class jikimo_bom_line(models.Model):
class ProductProduct(models.Model):
_inherit = 'product.product'
_order = 'cutting_tool_material_id, cutting_tool_type_id'
# stock_move_count = fields.Integer(string='stock_move count', compute='_compute_stock_move_count')
#
# @api.depends('stock_move_ids')
# def _compute_stock_move_count(self):
# for record in self:
# if record.stock_move_ids:
# record.stock_move_count = len(record.stock_move_ids)
# else:
# record.stock_move_count = 0
stock_move_count = fields.Integer(string='stock_move count', compute='_compute_stock_move_count', store=True)
@api.depends('stock_move_ids')
def _compute_stock_move_count(self):
for record in self:
if record.stock_move_ids:
record.stock_move_count = len(record.stock_move_ids)
else:
record.stock_move_count = 0
def search(self, args, offset=0, limit=None, order=None, count=False):
# 你可以在这里修改 `args` 以调整搜索条件

View File

@@ -14,7 +14,10 @@ class ToolInventory(models.Model):
self._bom_mainfest()
return self.bom_mainfest()
request.session['jikimo_bom_product'] = {'bom_id': int(self.jikimo_bom_ids)}
# context = dict(self.env.context)
# context.update({'jikimo_bom_product': self.jikimo_bom_ids.options})
# if self.functional_cutting_tool_model_id.cutting_tool_type_ids:
# context.update({'jikimo_bom_product_cutting_tool_type': self.functional_cutting_tool_model_id.cutting_tool_type_ids.ids})
return {
'type': 'ir.actions.act_window',
'name': '刀具组装清单',
@@ -23,6 +26,7 @@ class ToolInventory(models.Model):
'view_id': self.env.ref('sf_tool_management.view_jikimo_bom_form').id,
'res_id': int(self.jikimo_bom_ids),
'target': 'current', # Use 'new' to open in a new window/tab
# {'jikimo_bom_product': self.jikimo_bom_ids.options}
}
# 创建bom单

View File

@@ -9,7 +9,7 @@
<field name="name">jikimo.bom.form</field>
<field name="model">jikimo.bom</field>
<field name="arch" type="xml">
<form create="False">
<form>
<header>
<button type="action" name="%(action_jikimo_bom_wizard)d"
class="btn btn-info" string="组装方式.." context="{'default_bom_id':id}"
@@ -31,7 +31,7 @@
<notebook colspan="4">
<page string="物料清单">
<field name="product_ids" context="{'jikimo_bom_product': True}">
<tree create="False">
<tree>
<field name="name"/>
<!-- <field name="categ_id"/>-->
<field name="cutting_tool_material_id"/>

View File

@@ -6,32 +6,10 @@
<field name="inherit_id" ref="sf_base.view_tool_inventory_tree"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='extension']" position="before">
<field name="knife_handle_model" class="o-sticky-header"/>
<field name="knife_handle_model" />
<button name="bom_mainfest" string="bom清单" type="object" class="btn-link"
icon="fa-refresh"/>
icon="fa-refresh" />
</xpath>
</field>
</record>
<record id="view_tool_inventory_inherit_search" model="ir.ui.view">
<field name="name">sf.tool.inventory.inherit.search</field>
<field name="model">sf.tool.inventory</field>
<field name="inherit_id" ref="sf_base.view_cutting_tool_material_search"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='extension']" position="after">
<searchpanel>
<field name="functional_cutting_tool_model_id" enable_counters="1"/>
<!-- <field name="job_id" enable_counters="1"/>-->
<!-- <field name="department_id" enable_counters="1"/>-->
<!-- <field name="company_id" enable_counters="1"/>-->
</searchpanel>
</xpath>
</field>
</record>
<!-- <searchpanel>-->
<!-- <field name="org_type_id_display" enable_counters="1"/>-->
<!-- &lt;!&ndash; <field name="job_id" enable_counters="1"/>&ndash;&gt;-->
<!-- <field name="department_id" enable_counters="1"/>-->
<!-- &lt;!&ndash; <field name="company_id" enable_counters="1"/>&ndash;&gt;-->
<!-- </searchpanel>-->
</odoo>

View File

@@ -8,7 +8,7 @@
<field name="inherit_id" ref="sf_base.view_cutter_function_tree"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='name']" position="after">
<field name="cutting_tool_type_ids" widget="many2many_tags" options="{'no_create': True}"/>
<field name="cutting_tool_type_ids" widget="many2many_tags"/>
</xpath>
</field>
</record>

View File

@@ -15,24 +15,9 @@ class JikimoBomWizard(models.TransientModel):
('刀柄+刀杆+刀片', '刀柄+刀杆+刀片'),
('刀柄+刀盘+刀片', '刀柄+刀盘+刀片')
], string='组装方式', required=True)
# assembly_options_ids = fields.Many2many('sf.cutting.tool.material', string="组装方式")
is_ok = fields.Boolean('确认上述信息正确无误。')
@api.model
def default_get(self, fields):
res = super(JikimoBomWizard, self).default_get(fields)
# 根据某个字段的值设置默认选项
if 'default_bom_id' in self.env.context:
jikimo_bom = self.env['jikimo.bom'].browse(self.env.context['default_bom_id'])
if not jikimo_bom:
return res
if jikimo_bom.options:
res['assembly_options'] = jikimo_bom.options
# some_field_value = self.env.context.get('some_field')
# if some_field_value == 'condition_value':
# res['default_option'] = 'option2' # 设置为特定选项
return res
def submit(self):
if not self.bom_id:
raise UserError('缺少bom信息')

View File

@@ -931,6 +931,13 @@ class SfStockMoveLine(models.Model):
if not record.destination_location_id.product_id:
record.destination_location_id.product_id = record.product_id.id
@api.model_create_multi
def create(self, vals_list):
records = super(SfStockMoveLine, self).create(vals_list)
self.put_shelf_location(records)
return records
class SfStockPicking(models.Model):
_inherit = 'stock.picking'
@@ -1115,12 +1122,6 @@ class SfPickingType(models.Model):
'sf_warehouse.group_sf_stock_manager'
)
def _get_action(self, action_xmlid):
action = super(SfPickingType, self)._get_action(action_xmlid)
if not self.env.user.has_group('base.group_system'):
action['context']['create'] = False
return action
class CustomStockMove(models.Model):
_name = 'stock.move'

View File

@@ -4,7 +4,6 @@
<record model="ir.actions.act_window" id="stock.stock_picking_type_action">
<field name="context">{'search_default_groupby_code':1}</field>
<field name="domain">[('name', '!=', '制造')]</field>
</record>
<record id="view_location_form_sf_inherit" model="ir.ui.view">

View File

@@ -77,20 +77,18 @@ class ShelfLocationWizard(models.TransientModel):
def confirm_the_change(self):
if self.destination_barcode_id:
stocks = []
if self.lot_id:
self.current_barcode_id.product_sn_id = False
self.destination_barcode_id.product_sn_id = self.lot_id.id
stocks = self.create_stock_moves(self.lot_id, 1)
self.create_stock_moves(self.lot_id, 1)
elif self.current_product_sn_ids:
for current_product_sn_id in self.current_product_sn_ids:
stocks = self.create_stock_moves(current_product_sn_id.lot_id, current_product_sn_id.qty_num)
self.create_stock_moves(current_product_sn_id.lot_id, current_product_sn_id.qty_num)
current_product_sn_id.write({
'qty_num': 0
})
else:
raise ValidationError('没有需要变更的批次/序列号!')
self.env['stock.move.line'].sudo().put_shelf_location(stocks[-1])
else:
raise ValidationError('请选择目标货位编码!')