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'} } }