359 lines
17 KiB
Python
359 lines
17 KiB
Python
import logging
|
|
|
|
from datetime import timedelta, datetime, date
|
|
|
|
from odoo import api, fields, models, _
|
|
from odoo.exceptions import ValidationError
|
|
|
|
|
|
class ShelfLocation(models.Model):
|
|
_inherit = 'sf.shelf.location'
|
|
|
|
tool_rfid = fields.Char('Rfid', compute='_compute_tool', store=True)
|
|
tool_name_id = fields.Many2one('sf.functional.cutting.tool.entity', string='功能刀具名称', compute='_compute_tool',
|
|
store=True)
|
|
|
|
@api.depends('product_id')
|
|
def _compute_tool(self):
|
|
for item in self:
|
|
if item.product_id:
|
|
if item.product_id.categ_id.name == '功能刀具':
|
|
tool_id = self.env['sf.functional.cutting.tool.entity'].sudo().search(
|
|
[('barcode_id', '=', item.product_sn_id.id)])
|
|
if tool_id:
|
|
item.tool_rfid = tool_id.rfid
|
|
item.tool_name_id = tool_id.id
|
|
continue
|
|
item.tool_rfid = ''
|
|
item.tool_name_id = False
|
|
|
|
|
|
class StockMoveLine(models.Model):
|
|
_inherit = 'stock.move.line'
|
|
|
|
@api.model_create_multi
|
|
def create(self, vals_list):
|
|
records = super(StockMoveLine, self).create(vals_list)
|
|
move_lines = records.filtered(lambda a: a.product_id.categ_id.name == '功能刀具' and a.state == 'done')
|
|
if move_lines: # 校验是否为功能刀具移动历史
|
|
self.button_function_tool_use_verify(move_lines)
|
|
return records
|
|
|
|
def button_function_tool_use_verify(self, move_lines):
|
|
"""
|
|
对所有从【刀具房】到【制造前】的功能刀具进行校验(校验是否为制造订单所缺的刀)
|
|
"""
|
|
location_id = self.env['stock.location'].search([('name', '=', '刀具房')])
|
|
location_dest_id = self.env['stock.location'].search([('name', '=', '制造前')])
|
|
line_ids = move_lines.filtered(
|
|
lambda a: a.location_id == location_id and a.location_dest_id == location_dest_id)
|
|
for line_id in line_ids:
|
|
if line_id.lot_id:
|
|
self.env['sf.functional.cutting.tool.entity'].sudo().search(
|
|
[('barcode_id', '=', line_id.lot_id.id),
|
|
('functional_tool_status', '=', '正常')]).cnc_function_tool_use_verify()
|
|
|
|
for move_line in move_lines:
|
|
if move_line.lot_id:
|
|
tool_id = self.env['sf.functional.cutting.tool.entity'].sudo().search(
|
|
[('barcode_id', '=', move_line.lot_id.id),
|
|
('functional_tool_status', '=', '正常')])
|
|
tool_id.stock_num += tool_id.stock_num
|
|
|
|
|
|
class StockPicking(models.Model):
|
|
_inherit = 'stock.picking'
|
|
|
|
def button_validate(self):
|
|
res = super().button_validate()
|
|
move_lines = self.move_line_ids.filtered(lambda a: a.product_id.categ_id.name == '功能刀具')
|
|
if move_lines:
|
|
self.env['stock.move.line'].sudo().button_function_tool_use_verify(move_lines)
|
|
return res
|
|
|
|
def create_tool_stocking_picking(self, stock_lot, obj):
|
|
"""
|
|
创建功能刀具组装入库单
|
|
"""
|
|
# 获取名称为刀具组装入库的作业类型
|
|
picking_type_id = self.env['stock.picking.type'].sudo().search([('name', '=', '刀具组装入库')])
|
|
# 创建刀具组装入库单
|
|
picking_id = self.env['stock.picking'].create({
|
|
'name': self._get_name_stock(picking_type_id),
|
|
'picking_type_id': picking_type_id.id,
|
|
'location_id': picking_type_id.default_location_src_id.id,
|
|
'location_dest_id': picking_type_id.default_location_dest_id.id,
|
|
'origin': obj.assembly_order_code
|
|
})
|
|
# 创建作业详情对象记录,并绑定到刀具组装入库单
|
|
self.env['stock.move.line'].create({
|
|
'picking_id': picking_id.id,
|
|
'product_id': stock_lot.product_id.id,
|
|
'location_id': picking_id.location_id.id,
|
|
'location_dest_id': picking_id.location_dest_id.id,
|
|
'lot_id': stock_lot.id,
|
|
'install_tool_time': fields.Datetime.now(),
|
|
'qty_done': 1,
|
|
'functional_tool_name_id': obj.id,
|
|
'functional_tool_type_id': obj.functional_tool_type_id.id,
|
|
'diameter': obj.after_assembly_functional_tool_diameter,
|
|
'knife_tip_r_angle': obj.after_assembly_knife_tip_r_angle,
|
|
'code': obj.code,
|
|
'rfid': obj.rfid,
|
|
'functional_tool_name': obj.after_assembly_functional_tool_name,
|
|
'tool_groups_id': obj.tool_groups_id.id
|
|
})
|
|
# 将刀具组装入库单的状态更改为就绪
|
|
picking_id.action_confirm()
|
|
picking_id.button_validate()
|
|
|
|
def _get_name_stock(self, picking_type_id):
|
|
name = picking_type_id.sequence_id.prefix + str(
|
|
datetime.strptime(str(fields.Date.today()), "%Y-%m-%d").strftime("%Y%m%d"))
|
|
stock_id = self.env['stock.picking'].sudo().search(
|
|
[('name', 'like', name), ('picking_type_id', '=', picking_type_id.id)],
|
|
limit=1,
|
|
order="id desc"
|
|
)
|
|
if not stock_id:
|
|
num = "%03d" % 1
|
|
else:
|
|
m = int(stock_id.name[-3:]) + 1
|
|
num = "%03d" % m
|
|
return name + str(num)
|
|
|
|
def tool_location_num(self, freight_barcode_id, lot_id):
|
|
location_lot = self.env['sf.shelf.location.lot'].sudo().search([('lot_id', '=', lot_id.id), (
|
|
'shelf_location_id', '=', freight_barcode_id.id)])
|
|
if not location_lot:
|
|
raise ValidationError(
|
|
f'[{freight_barcode_id.barcode}]货位的[{lot_id.name}]批次物料已用完,请重新选择!')
|
|
|
|
def create_tool_stocking_picking1(self, obj):
|
|
"""
|
|
创建刀具物料出库单
|
|
"""
|
|
# 获取名称为内部调拨的作业类型
|
|
picking_type_id = self.env['stock.picking.type'].sudo().search([('name', '=', '内部调拨')])
|
|
# 创建刀具物料出库单
|
|
picking_id = self.env['stock.picking'].create({
|
|
'name': self._get_name_stock1(picking_type_id),
|
|
'picking_type_id': picking_type_id.id,
|
|
'location_id': self.env['stock.location'].search([('name', '=', '刀具房')]).id,
|
|
'location_dest_id': self.env['stock.location'].search([('name', '=', '刀具组装位置')]).id,
|
|
'origin': obj.assembly_order_code
|
|
})
|
|
# =============刀具物料出库===================
|
|
stock_move_id = self.env['stock.move']
|
|
datas = {'data': [], 'picking_id': picking_id}
|
|
if obj.handle_code_id:
|
|
if obj.handle_code_id.tool_material_status == '在用':
|
|
raise ValidationError(f'Rfid为{obj.handle_code_id.rfid}的刀柄已被使用,请重新选择!')
|
|
# 修改刀柄序列号状态为【在用】
|
|
obj.handle_code_id.sudo().write({'tool_material_status': '在用'})
|
|
datas['data'].append(
|
|
{'current_location_id': self.env['sf.shelf.location'], 'lot_id': obj.handle_code_id})
|
|
if obj.integral_product_id:
|
|
self.tool_location_num(obj.integral_freight_barcode_id, obj.integral_lot_id)
|
|
datas['data'].append(
|
|
{'current_location_id': obj.integral_freight_barcode_id, 'lot_id': obj.integral_lot_id})
|
|
if obj.blade_product_id:
|
|
self.tool_location_num(obj.blade_freight_barcode_id, obj.blade_lot_id)
|
|
datas['data'].append(
|
|
{'current_location_id': obj.blade_freight_barcode_id, 'lot_id': obj.blade_lot_id})
|
|
if obj.bar_product_id:
|
|
self.tool_location_num(obj.bar_freight_barcode_id, obj.bar_lot_id)
|
|
datas['data'].append(
|
|
{'current_location_id': obj.bar_freight_barcode_id, 'lot_id': obj.bar_lot_id})
|
|
if obj.pad_product_id:
|
|
self.tool_location_num(obj.pad_freight_barcode_id, obj.pad_lot_id)
|
|
datas['data'].append(
|
|
{'current_location_id': obj.pad_freight_barcode_id, 'lot_id': obj.pad_lot_id})
|
|
if obj.chuck_product_id:
|
|
self.tool_location_num(obj.chuck_freight_barcode_id, obj.chuck_lot_id)
|
|
datas['data'].append(
|
|
{'current_location_id': obj.chuck_freight_barcode_id, 'lot_id': obj.chuck_lot_id})
|
|
# 创建刀具物料出库库存移动记录
|
|
stock_move_id.create_tool_material_stock_moves(datas)
|
|
# 将刀具物料出库库单的状态更改为就绪
|
|
picking_id.action_confirm()
|
|
# 修改刀具物料出库移动历史记录
|
|
stock_move_id.write_tool_material_stock_move_lines(datas)
|
|
# 设置数量,并验证完成
|
|
picking_id.action_set_quantities_to_reservation()
|
|
picking_id.button_validate()
|
|
logging.info(f'刀具物料调拨单状态:{picking_id.state}')
|
|
|
|
def _get_name_stock1(self, picking_type_id):
|
|
name = f'{picking_type_id.sequence_id.prefix}DJ/{date.today().strftime("%y")}'
|
|
stock_id = self.env['stock.picking'].sudo().search(
|
|
[('name', 'like', name), ('picking_type_id', '=', picking_type_id.id)],
|
|
limit=1,
|
|
order="id desc"
|
|
)
|
|
if not stock_id:
|
|
num = "%05d" % 1
|
|
else:
|
|
m = int(stock_id.name[-5:]) + 1
|
|
num = "%05d" % m
|
|
return name + str(num)
|
|
|
|
|
|
class StockMove(models.Model):
|
|
_inherit = 'stock.move'
|
|
|
|
def create_tool_material_stock_moves(self, datas):
|
|
picking_id = datas['picking_id']
|
|
data = datas['data']
|
|
stock_move_ids = []
|
|
for res in data:
|
|
if res:
|
|
if res['lot_id'].product_qty <= 0:
|
|
raise ValidationError(
|
|
f'[{res["lot_id"].product_id.name}产品的{res["lot_id"].name}]批次/序列号库存不足!')
|
|
# 创建库存移动记录
|
|
stock_move_id = self.env['stock.move'].sudo().create({
|
|
'name': picking_id.name,
|
|
'picking_id': picking_id.id,
|
|
'product_id': res['lot_id'].product_id.id,
|
|
'location_id': picking_id.location_id.id,
|
|
'location_dest_id': picking_id.location_dest_id.id,
|
|
'product_uom_qty': 1.00,
|
|
'reserved_availability': 1.00
|
|
})
|
|
stock_move_ids.append(stock_move_id)
|
|
return stock_move_ids
|
|
|
|
def write_tool_material_stock_move_lines(self, datas):
|
|
picking_id = datas['picking_id']
|
|
data = datas['data']
|
|
move_line_ids = picking_id.move_line_ids
|
|
for move_line_id in move_line_ids:
|
|
for res in data:
|
|
if move_line_id.lot_id.product_id == res['lot_id'].product_id:
|
|
move_line_id.write({
|
|
'current_location_id': res.get('current_location_id').id,
|
|
'lot_id': res.get('lot_id').id
|
|
})
|
|
return True
|
|
|
|
|
|
class ProductProduct(models.Model):
|
|
_inherit = 'product.product'
|
|
|
|
def create_assemble_warehouse_receipt(self, obj):
|
|
"""
|
|
创建功能刀具批次/序列号记录
|
|
"""
|
|
product_id = self.env['product.product'].search([('categ_type', '=', '功能刀具'), ('tracking', '=', 'serial')])
|
|
if not product_id:
|
|
logging.info('没有搜索到功能刀具产品:%s' % product_id)
|
|
raise ValidationError('没有找到按唯一序列号追溯的功能刀具产品信息!')
|
|
stock_lot = self.env['stock.lot'].create({
|
|
'name': self.get_stock_lot_name(obj),
|
|
'product_id': product_id[0].id,
|
|
'company_id': self.env.company.id
|
|
})
|
|
return stock_lot
|
|
|
|
def get_stock_lot_name(self, obj):
|
|
"""
|
|
生成功能刀具序列号
|
|
"""
|
|
company = obj.cutting_tool_cutterhandle_model_id.code.split('-', 1)[0]
|
|
new_time = datetime.strptime(str(fields.Date.today()), "%Y-%m-%d").strftime("%Y%m%d")
|
|
code = '%s-GNDJ-%s-%s' % (company, obj.after_assembly_functional_tool_type_id.code, new_time)
|
|
stock_lot_id = self.env['stock.lot'].sudo().search(
|
|
[('name', 'like', code)], limit=1, order="id desc")
|
|
if not stock_lot_id:
|
|
num = "%03d" % 1
|
|
else:
|
|
m = int(stock_lot_id.name[-3:]) + 1
|
|
num = "%03d" % m
|
|
return '%s-%s' % (code, num)
|
|
|
|
def set_tool_material(self):
|
|
tool_id = self.env.context.get('tool_id')
|
|
tool_assembly_id = self.env['sf.functional.tool.assembly'].sudo().search([('id', '=', tool_id)])
|
|
if len(self) > 1:
|
|
raise ValidationError('请不要多选')
|
|
else:
|
|
tool_assembly_id.handle_product_id = self.id
|
|
tool_assembly_id.handle_code_id = False
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {
|
|
'message': '刀柄信息更改成功',
|
|
'type': 'success',
|
|
'next': {'type': 'ir.actions.act_window_close'}
|
|
}
|
|
}
|
|
|
|
|
|
class SfShelfLocationLot(models.Model):
|
|
_inherit = 'sf.shelf.location.lot'
|
|
|
|
product_id = fields.Many2one('product.product', '产品', compute='_compute_product_id', store=True)
|
|
cutting_tool_type = fields.Char(string="刀具物料类型", compute='_compute_product_id', store=True)
|
|
cutting_tool_type_id = fields.Many2one('sf.cutting.tool.type', string='类型',
|
|
related='product_id.cutting_tool_type_id')
|
|
cutting_tool_model_id = fields.Many2one('sf.cutting_tool.standard.library', string='型号名称',
|
|
related='product_id.cutting_tool_model_id')
|
|
specification_id = fields.Many2one('sf.tool.materials.basic.parameters', string='物料号',
|
|
related='product_id.specification_id')
|
|
brand_id = fields.Many2one('sf.machine.brand', '品牌', related='product_id.brand_id')
|
|
|
|
cutting_tool_blade_diameter = fields.Float('刃部直径(mm)', related='product_id.cutting_tool_blade_diameter')
|
|
cutting_tool_blade_tip_working_size = fields.Float('刀尖R角(mm)',
|
|
related='product_id.cutting_tool_blade_tip_r_size')
|
|
cutting_tool_blade_radius = fields.Char('刀尖圆弧半径(mm)',
|
|
related='product_id.cutting_tool_blade_tip_circular_arc_radius')
|
|
cutting_tool_cutter_arbor_diameter = fields.Float('刀杆直径(mm)',
|
|
related='product_id.cutting_tool_cutter_arbor_diameter')
|
|
cutting_tool_cutter_head_diameter = fields.Float('刀盘直径(mm)',
|
|
related='product_id.cutting_tool_cutter_head_diameter')
|
|
|
|
fit_blade_shape_id = fields.Many2one('maintenance.equipment.image', '适配刀片形状',
|
|
related='product_id.fit_blade_shape_id')
|
|
|
|
@api.depends('lot_id')
|
|
def _compute_product_id(self):
|
|
for item in self:
|
|
if item.lot_id:
|
|
item.product_id = item.lot_id.product_id.id
|
|
item.cutting_tool_type = item.lot_id.product_id.cutting_tool_type
|
|
|
|
def set_tool_material(self):
|
|
tool_type = self.env.context.get('tool_type')
|
|
tool_id = self.env.context.get('tool_id')
|
|
tool_assembly_id = self.env['sf.functional.tool.assembly'].sudo().search([('id', '=', tool_id)])
|
|
if len(self) > 1:
|
|
raise ValidationError('请不要多选')
|
|
if tool_type == '整体式刀具':
|
|
tool_assembly_id.integral_freight_barcode_id = self.shelf_location_id.id
|
|
tool_assembly_id.integral_lot_id = self.lot_id.id
|
|
tool_assembly_id.integral_verify = False
|
|
elif tool_type == '刀片':
|
|
tool_assembly_id.blade_freight_barcode_id = self.shelf_location_id.id
|
|
tool_assembly_id.blade_lot_id = self.lot_id.id
|
|
tool_assembly_id.blade_verify = False
|
|
elif tool_type == '刀杆':
|
|
tool_assembly_id.bar_freight_barcode_id = self.shelf_location_id.id
|
|
tool_assembly_id.bar_lot_id = self.lot_id.id
|
|
tool_assembly_id.bar_verify = False
|
|
elif tool_type == '刀盘':
|
|
tool_assembly_id.pad_freight_barcode_id = self.shelf_location_id.id
|
|
tool_assembly_id.pad_lot_id = self.lot_id.id
|
|
tool_assembly_id.pad_verify = False
|
|
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {
|
|
'message': f'[{tool_type}]物料信息更改成功',
|
|
'type': 'success',
|
|
'next': {'type': 'ir.actions.act_window_close'}
|
|
}
|
|
}
|