diff --git a/sf_base/models/common.py b/sf_base/models/common.py index 6fd7d814..888f3eaf 100644 --- a/sf_base/models/common.py +++ b/sf_base/models/common.py @@ -61,12 +61,10 @@ class MrsMaterialModel(models.Model): supplier_ids = fields.One2many('sf.supplier.sort', 'materials_model_id', string='供应商') active = fields.Boolean('有效', default=True) - @api.onchange('gain_way') - def _check_gain_way(self): - if not self.gain_way: - raise UserError("请选择获取方式") - if self.gain_way in ['外协', '采购']: - if not self.supplier_ids: + @api.constrains("gain_way") + def _check_supplier_ids(self): + for item in self: + if item.gain_way in ('外协', '采购') and not item.supplier_ids: raise UserError("请添加供应商") diff --git a/sf_base/views/common_view.xml b/sf_base/views/common_view.xml index 74e916a8..a7cbcf9e 100644 --- a/sf_base/views/common_view.xml +++ b/sf_base/views/common_view.xml @@ -251,7 +251,7 @@ - + @@ -270,9 +270,9 @@ - + - + diff --git a/sf_dlm_management/data/stock_data.xml b/sf_dlm_management/data/stock_data.xml index 3d2c4527..be2a36e0 100644 --- a/sf_dlm_management/data/stock_data.xml +++ b/sf_dlm_management/data/stock_data.xml @@ -22,6 +22,16 @@ + + 拆解 + + internal + DJCJ + true + true + + + 刀具组装入库 diff --git a/sf_sale/models/sale_order.py b/sf_sale/models/sale_order.py index f47f8c8d..2791c760 100644 --- a/sf_sale/models/sale_order.py +++ b/sf_sale/models/sale_order.py @@ -256,33 +256,33 @@ class ResPartnerToSale(models.Model): # if obj: # raise UserError('该邮箱已存在,请重新输入') - @api.model - def _name_search(self, name, args=None, operator='ilike', limit=100, name_get_uid=None): - if self._context.get('is_customer'): - if self.env.user.has_group('sf_base.group_sale_director'): - domain = [('customer_rank', '>', 0)] - elif self.env.user.has_group('sf_base.group_sale_salemanager'): - customer = self.env['res.partner'].search( - [('customer_rank', '>', 0), ('user_id', '=', self.env.user.id)]) - if customer: - ids = [t.id for t in customer] - domain = [('id', 'in', ids)] - else: - domain = [('id', '=', False)] - return self._search(domain, limit=limit, access_rights_uid=name_get_uid) - elif self._context.get('is_supplier') or self.env.user.has_group('sf_base.group_purchase_director'): - if self.env.user.has_group('sf_base.group_purchase_director'): - domain = [('supplier_rank', '>', 0)] - elif self.env.user.has_group('sf_base.group_purchase'): - supplier = self.env['res.partner'].search( - [('supplier_rank', '>', 0), ('purchase_user_id', '=', self.env.user.id)]) - if supplier: - ids = [t.id for t in supplier] - domain = [('id', 'in', ids)] - else: - domain = [('id', '=', False)] - return self._search(domain, limit=limit, access_rights_uid=name_get_uid) - return super()._name_search(name, args, operator, limit, name_get_uid) + # @api.model + # def _name_search(self, name, args=None, operator='ilike', limit=100, name_get_uid=None): + # if self._context.get('is_customer'): + # if self.env.user.has_group('sf_base.group_sale_director'): + # domain = [('customer_rank', '>', 0)] + # elif self.env.user.has_group('sf_base.group_sale_salemanager'): + # customer = self.env['res.partner'].search( + # [('customer_rank', '>', 0), ('user_id', '=', self.env.user.id)]) + # if customer: + # ids = [t.id for t in customer] + # domain = [('id', 'in', ids)] + # else: + # domain = [('id', '=', False)] + # return self._search(domain, limit=limit, access_rights_uid=name_get_uid) + # elif self._context.get('is_supplier') or self.env.user.has_group('sf_base.group_purchase_director'): + # if self.env.user.has_group('sf_base.group_purchase_director'): + # domain = [('supplier_rank', '>', 0)] + # elif self.env.user.has_group('sf_base.group_purchase'): + # supplier = self.env['res.partner'].search( + # [('supplier_rank', '>', 0), ('purchase_user_id', '=', self.env.user.id)]) + # if supplier: + # ids = [t.id for t in supplier] + # domain = [('id', 'in', ids)] + # else: + # domain = [('id', '=', False)] + # return self._search(domain, limit=limit, access_rights_uid=name_get_uid) + # return super()._name_search(name, args, operator, limit, name_get_uid) @api.onchange('user_id') def _get_salesman(self): diff --git a/sf_tool_management/models/base.py b/sf_tool_management/models/base.py index 182141cf..b77e6469 100644 --- a/sf_tool_management/models/base.py +++ b/sf_tool_management/models/base.py @@ -669,6 +669,20 @@ class FunctionalToolAssembly(models.Model): :return: """ + picking_num = fields.Integer('调拨单数量', compute='compute_picking_num', store=True) + + @api.depends('assemble_status') + def compute_picking_num(self): + for item in self: + picking_ids = self.env['stock.picking'].sudo().search([('origin', '=', item.assembly_order_code)]) + item.picking_num = len(picking_ids) + + def open_tool_stock_picking(self): + action = self.env.ref('stock.action_picking_tree_all') + result = action.read()[0] + result['domain'] = [('origin', '=', self.assembly_order_code)] + return result + @api.model_create_multi def create(self, vals): obj = super(FunctionalToolAssembly, self).create(vals) @@ -758,7 +772,7 @@ class FunctionalToolDismantle(models.Model): num = "%03d" % m return 'GNDJ-CJD-%s-%s' % (datetime, num) - functional_tool_id = fields.Many2one('sf.functional.cutting.tool.entity', '功能刀具', required=True, + functional_tool_id = fields.Many2one('sf.functional.cutting.tool.entity', '功能刀具', required=True, tracking=True, domain=[('functional_tool_status', '!=', '已拆除'), ('current_location', '=', '刀具房')]) tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型', store=True, @@ -776,8 +790,18 @@ class FunctionalToolDismantle(models.Model): dismantle_person = fields.Char('拆解人', readonly=True) image = fields.Binary('图片', readonly=True) - scrap_id = fields.Char('报废单号', readonly=True) + scrap_ids = fields.One2many('stock.scrap', 'functional_tool_dismantle_id', string='报废单号', readonly=True) grinding_id = fields.Char('磨削单号', readonly=True) + picking_id = fields.Many2one('stock.picking', string='刀具物料调拨单') + picking_num = fields.Integer('调拨单数量', default=0, compute='compute_picking_num', store=True) + + @api.depends('picking_id') + def compute_picking_num(self): + for item in self: + if item.picking_id: + item.picking_num = 1 + else: + item.picking_num = 0 state = fields.Selection([('待拆解', '待拆解'), ('已拆解', '已拆解')], default='待拆解', tracking=True) active = fields.Boolean('有效', default=True) @@ -791,7 +815,14 @@ class FunctionalToolDismantle(models.Model): handle_rfid = fields.Char(string='刀柄Rfid', compute='_compute_functional_tool_num', store=True) handle_lot_id = fields.Many2one('stock.lot', string='刀柄序列号', compute='_compute_functional_tool_num', store=True) - scrap_boolean = fields.Boolean(string='刀柄是否报废', default=False, tracking=True) + scrap_boolean = fields.Boolean(string='刀柄是否报废', default=False, tracking=True, compute='compute_scrap_boolean', + store=True) + + @api.depends('dismantle_cause') + def compute_scrap_boolean(self): + for item in self: + if item.dismantle_cause not in ['寿命到期报废', '崩刀报废']: + item.scrap_boolean = False # 整体式 integral_product_id = fields.Many2one('product.product', string='整体式刀具', @@ -933,75 +964,50 @@ class FunctionalToolDismantle(models.Model): self.rfid, self.functional_tool_id.current_location)) # 目标重复校验 self.location_duplicate_check() - location = self.env['stock.location'].search([('name', '=', '刀具组装位置')]) - location_dest = self.env['stock.location'].search([('name', '=', '刀具房')]) + datas = {'scrap': [], 'picking': []} # =================刀柄是否[报废]拆解======= - location_dest_scrap_ids = self.env['stock.location'].search([('name', 'in', ('Scrap', '报废'))]) if self.handle_rfid: lot = self.env['stock.lot'].sudo().search([('rfid', '=', self.handle_rfid)]) if not lot: - raise ValidationError('Rfid为【%s】的功能刀具序列号不存在!' % self.handle_rfid) - functional_tool_assembly = self.functional_tool_id.functional_tool_name_id + raise ValidationError('Rfid为【%s】的刀柄序列号不存在!' % self.handle_rfid) if self.scrap_boolean: # 刀柄报废 入库到Scrap - lot.create_stock_quant(location, location_dest_scrap_ids[-1], False, code, False, False) + datas['scrap'].append({'lot_id': lot}) lot.tool_material_status = '报废' else: # 刀柄不报废 入库到刀具房 - lot.create_stock_quant(location, location_dest, False, code, False, False) + datas['picking'].append({'lot_id': lot, 'destination': self.env['sf.shelf.location']}) lot.tool_material_status = '可用' - # ==============功能刀具[报废]拆解================ if self.dismantle_cause in ['寿命到期报废', '崩刀报废']: # 除刀柄外物料报废 入库到Scrap if self.integral_product_id: - self.integral_product_id.dismantle_stock_moves(False, self.integral_lot_id, location, - location_dest_scrap_ids[-1], code) + datas['scrap'].append({'lot_id': self.integral_lot_id}) elif self.blade_product_id: - self.blade_product_id.dismantle_stock_moves(False, self.blade_lot_id, location, - location_dest_scrap_ids[-1], code) + datas['scrap'].append({'lot_id': self.blade_lot_id}) if self.bar_product_id: - self.bar_product_id.dismantle_stock_moves(False, self.bar_lot_id, location, - location_dest_scrap_ids[-1], code) + datas['scrap'].append({'lot_id': self.bar_lot_id}) elif self.pad_product_id: - self.pad_product_id.dismantle_stock_moves(False, self.pad_lot_id, location, - location_dest_scrap_ids[-1], code) + datas['scrap'].append({'lot_id': self.pad_lot_id}) if self.chuck_product_id: - self.chuck_product_id.dismantle_stock_moves(False, self.chuck_lot_id, location, - location_dest_scrap_ids[-1], code) - # ===========功能刀具[磨削]拆解============== - # elif self.dismantle_cause in ['刀具需磨削']: - # location_dest = self.env['stock.location'].search([('name', '=', '磨削房')]) - # # 除刀柄外物料拆解 入库到具体库位 - # if self.integral_product_id: - # self.integral_product_id.dismantle_stock_moves(False, location, location_dest) - # elif self.blade_product_id: - # self.blade_product_id.dismantle_stock_moves(False, location, location_dest) - # if self.bar_product_id: - # self.bar_product_id.dismantle_stock_moves(False, location, location_dest) - # elif self.pad_product_id: - # self.pad_product_id.dismantle_stock_moves(False, location, location_dest) - # if self.chuck_product_id: - # self.chuck_product_id.dismantle_stock_moves(False, location, location_dest) + datas['scrap'].append({'lot_id': self.chuck_lot_id}) # ==============功能刀具[更换,磨削]拆解============== elif self.dismantle_cause in ['更换为其他刀具', '刀具需磨削']: # 除刀柄外物料拆解 入库到具体货位 if self.integral_freight_id: - self.integral_product_id.dismantle_stock_moves(self.integral_freight_id, self.integral_lot_id, location, - location_dest, code) + datas['picking'].append({'lot_id': self.integral_lot_id, 'destination': self.integral_freight_id}) elif self.blade_freight_id: - self.blade_product_id.dismantle_stock_moves(self.blade_freight_id, self.blade_lot_id, location, - location_dest, code) + datas['picking'].append({'lot_id': self.blade_lot_id, 'destination': self.blade_freight_id}) if self.bar_freight_id: - self.bar_product_id.dismantle_stock_moves(self.bar_freight_id, self.bar_lot_id, location, - location_dest, code) + datas['picking'].append({'lot_id': self.bar_lot_id, 'destination': self.bar_freight_id}) elif self.pad_freight_id: - self.pad_product_id.dismantle_stock_moves(self.pad_freight_id, self.pad_lot_id, location, - location_dest, code) + datas['picking'].append({'lot_id': self.pad_lot_id, 'destination': self.pad_freight_id}) if self.chuck_freight_id: - self.chuck_product_id.dismantle_stock_moves(self.chuck_freight_id, self.chuck_lot_id, location, - location_dest, code) - # ===============删除功能刀具的Rfid字段的值, 赋值给Rfid(已拆解)字段===== + datas['picking'].append({'lot_id': self.chuck_lot_id, 'destination': self.chuck_freight_id}) + self.create_tool_picking_scrap(datas) + # ===============创建功能刀具拆解移动记录===== + self.env['stock.move'].create_functional_tool_stock_move(self) + # 修改功能刀具数据 self.functional_tool_id.write({ 'rfid_dismantle': self.functional_tool_id.rfid, 'rfid': '', @@ -1009,37 +1015,173 @@ class FunctionalToolDismantle(models.Model): }) # 修改拆解单的值 self.write({ - 'rfid_dismantle': self.rfid, 'dismantle_data': fields.Datetime.now(), 'dismantle_person': self.env.user.name, - 'rfid': '', + 'rfid': '%s(已拆解)' % self.rfid, 'state': '已拆解' }) logging.info('【%s】刀具拆解成功!' % self.name) + def create_tool_picking_scrap(self, datas): + scrap_data = datas['scrap'] + picking_data = datas['picking'] + if scrap_data: + for data in scrap_data: + if data: + self.env['stock.scrap'].create_tool_dismantle_stock_scrap(data['lot_id'], self) + if picking_data: + picking_id = self.env['stock.picking'].create_tool_dismantle_picking(self) + self.picking_id = picking_id.id + self.env['stock.move'].create_tool_stock_move({'data': picking_data, 'picking_id': picking_id}) + # 将刀具物料出库库单的状态更改为就绪 + picking_id.action_confirm() + # 修改刀具物料出库移动历史记录 + self.env['stock.move'].write_tool_stock_move_line({'data': picking_data, 'picking_id': picking_id}) + # 设置数量,并验证完成 + picking_id.action_set_quantities_to_reservation() + picking_id.button_validate() -class ProductProduct(models.Model): - _inherit = 'product.product' + def action_open_reference1(self): + self.ensure_one() + return { + 'res_model': self._name, + 'type': 'ir.actions.act_window', + 'views': [[False, "form"]], + 'res_id': self.id, + } - def dismantle_stock_moves(self, shelf_location_id, lot_id, location_id, location_dest_id, code): - # 创建功能刀具拆解单产品库存移动记录 - stock_move_id = self.env['stock.move'].sudo().create({ - 'name': code, - 'product_id': self.id, + def open_function_tool_stock_move_line(self): + action = self.env.ref('sf_tool_management.sf_inbound_and_outbound_records_of_functional_tools_view_act') + result = action.read()[0] + result['domain'] = [('functional_tool_dismantle_id', '=', self.id), ('qty_done', '>', 0)] + return result + + def open_tool_stock_picking(self): + action = self.env.ref('stock.action_picking_tree_all') + result = action.read()[0] + result['domain'] = [('origin', '=', self.code)] + return result + + +class StockPicking(models.Model): + _inherit = 'stock.picking' + + def create_tool_dismantle_picking(self, obj): + """ + 创建刀具物料入库单 + """ + # 获取名称为内部调拨的作业类型 + picking_type_id = self.env['stock.picking.type'].sudo().search([('name', '=', '内部调拨')]) + location_id = self.env['stock.location'].search([('name', '=', '刀具组装位置')]) + location_dest_id = self.env['stock.location'].search([('name', '=', '刀具房')]) + if not location_id: + raise ValidationError('缺少名称为【刀具组装位置】的仓库管理地点') + if not location_dest_id: + raise ValidationError('缺少名称为【刀具房】的仓库管理地点') + # 创建刀具物料出库单 + picking_id = self.env['stock.picking'].create({ + 'name': self._get_name_stock1(picking_type_id), + 'picking_type_id': picking_type_id.id, 'location_id': location_id.id, 'location_dest_id': location_dest_id.id, + 'origin': obj.code + }) + + return picking_id + + +class StockMove(models.Model): + _inherit = 'stock.move' + + def create_tool_stock_move(self, datas): + picking_id = datas['picking_id'] + data = datas['data'] + stock_move_ids = [] + for res in data: + if res: + # 创建库存移动记录 + 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_stock_move_line(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({ + 'destination_location_id': res.get('destination').id, + 'lot_id': res.get('lot_id').id + }) + return True + + def create_functional_tool_stock_move(self, dismantle_id): + """ + 对功能刀具拆解过程的功能刀具进行库存移动,以及创建移动历史 + """ + location_dismantle_id = self.env['stock.location'].search([('name', '=', '拆解')]) + if not location_dismantle_id: + raise ValidationError('缺少名称为【拆解】的仓库管理地点') + tool_id = dismantle_id.functional_tool_id + # 创建库存移动记录 + stock_move_id = self.env['stock.move'].sudo().create({ + 'name': dismantle_id.code, + 'product_id': tool_id.barcode_id.product_id.id, + 'location_id': tool_id.current_location_id.id, + 'location_dest_id': location_dismantle_id.id, 'product_uom_qty': 1.00, 'state': 'done' }) + # 创建移动历史记录 stock_move_line_id = self.env['stock.move.line'].sudo().create({ - 'product_id': self.id, - 'lot_id': lot_id.id, + 'product_id': tool_id.barcode_id.product_id.id, + 'functional_tool_dismantle_id': dismantle_id.id, + 'lot_id': tool_id.barcode_id.id, 'move_id': stock_move_id.id, - 'destination_location_id': shelf_location_id.id if shelf_location_id else False, - 'install_tool_time': fields.Datetime.now(), 'qty_done': 1.0, 'state': 'done', + 'functional_tool_type_id': tool_id.sf_cutting_tool_type_id.id, + 'diameter': tool_id.functional_tool_diameter, + 'knife_tip_r_angle': tool_id.knife_tip_r_angle, + 'code': tool_id.code, + 'rfid': tool_id.rfid, + 'functional_tool_name': tool_id.name, + 'tool_groups_id': tool_id.tool_groups_id.id }) - return stock_move_id, stock_move_line_id + + +class CustomStockScrap(models.Model): + _inherit = 'stock.scrap' + + functional_tool_dismantle_id = fields.Many2one('sf.functional.tool.dismantle', string="功能刀具拆解单") + + def create_tool_dismantle_stock_scrap(self, lot, dismantle_id): + location_id = self.env['stock.location'].search([('name', '=', '刀具组装位置')]) + scrap_location_id = self.env['stock.location'].search([('name', 'in', ('Scrap', '报废'))]) + if not location_id: + raise ValidationError('缺少名称为【刀具组装位置】的仓库管理地点') + if not scrap_location_id: + raise ValidationError('缺少名称为【Scrap】或【Scrap】的仓库管理地点') + stock_scrap_id = self.create({ + 'product_id': lot.product_id.id, + 'lot_id': lot.id, + 'location_id': location_id.id, + 'scrap_location_id': scrap_location_id.id, + 'functional_tool_dismantle_id': dismantle_id.id, + 'origin': dismantle_id.code + }) + # 完成报废单 + stock_scrap_id.action_validate() + return stock_scrap_id diff --git a/sf_tool_management/models/functional_tool.py b/sf_tool_management/models/functional_tool.py index 18f0a466..4e791fe5 100644 --- a/sf_tool_management/models/functional_tool.py +++ b/sf_tool_management/models/functional_tool.py @@ -252,9 +252,7 @@ class FunctionalCuttingToolEntity(models.Model): def open_safety_stock(self): action = self.env.ref('sf_tool_management.sf_real_time_distribution_of_functional_tools_view_act') result = action.read()[0] - result['domain'] = [('name', '=', self.name), ('diameter', '=', self.functional_tool_diameter), - ('knife_tip_r_angle', '=', self.knife_tip_r_angle), - ('coarse_middle_thin', '=', self.coarse_middle_thin)] + result['domain'] = [('id', '=', self.safe_inventory_id.id)] return result def tool_inventory_displacement_out(self): @@ -372,6 +370,7 @@ class StockMoveLine(models.Model): _order = 'date desc' functional_tool_name_id = fields.Many2one('sf.functional.tool.assembly', string='功能刀具组装单') + functional_tool_dismantle_id = fields.Many2one('sf.functional.tool.dismantle', string='功能刀具拆解单') functional_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型', store=True, group_expand='_read_group_functional_tool_type_id') functional_tool_name = fields.Char('刀具名称') @@ -392,6 +391,9 @@ class StockMoveLine(models.Model): if self.functional_tool_name_id: action = self.functional_tool_name_id.action_open_reference1() return action + if self.functional_tool_dismantle_id: + action = self.functional_tool_dismantle_id.action_open_reference1() + return action elif self.move_id: action = self.move_id.action_open_reference() if action['res_model'] != 'stock.move': @@ -409,13 +411,14 @@ class RealTimeDistributionOfFunctionalTools(models.Model): _inherit = ['mail.thread'] _description = '功能刀具安全库存' - name = fields.Char('名称', readonly=True, compute='_compute_name', store=True) + name = fields.Char('名称', compute='_compute_num', store=True) functional_name_id = fields.Many2one('sf.tool.inventory', string='功能刀具名称', required=True) - tool_groups_id = fields.Many2one('sf.tool.groups', '刀具组', readonly=False, required=True) - sf_cutting_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型', readonly=False, - group_expand='_read_mrs_cutting_tool_type_ids', store=True) - diameter = fields.Float(string='刀具直径(mm)', readonly=False) - knife_tip_r_angle = fields.Float(string='刀尖R角(mm)', readonly=False) + tool_groups_id = fields.Many2one('sf.tool.groups', '刀具组', compute='_compute_num', store=True) + sf_cutting_tool_type_id = fields.Many2one('sf.functional.cutting.tool.model', string='功能刀具类型', + compute='_compute_num', store=True, + group_expand='_read_mrs_cutting_tool_type_ids') + diameter = fields.Float(string='刀具直径(mm)', compute='_compute_num', store=True) + knife_tip_r_angle = fields.Float(string='刀尖R角(mm)', compute='_compute_num', store=True) tool_stock_num = fields.Integer(string='刀具房数量', compute='_compute_stock_num', store=True) side_shelf_num = fields.Integer(string='线边刀库数量', compute='_compute_stock_num', store=True) on_tool_stock_num = fields.Integer(string='机内刀库数量', compute='_compute_stock_num', store=True) @@ -460,22 +463,18 @@ class RealTimeDistributionOfFunctionalTools(models.Model): active = fields.Boolean(string='已归档', default=True) - @api.onchange('functional_name_id') - def _onchange_num(self): + @api.depends('functional_name_id', 'functional_name_id.diameter', 'functional_name_id.angle', + 'functional_name_id.functional_cutting_tool_model_id') + def _compute_num(self): for item in self: if item.functional_name_id: item.tool_groups_id = item.functional_name_id.tool_groups_id.id item.sf_cutting_tool_type_id = item.functional_name_id.functional_cutting_tool_model_id.id item.diameter = item.functional_name_id.diameter item.knife_tip_r_angle = item.functional_name_id.angle - - @api.depends('functional_name_id') - def _compute_name(self): - for obj in self: - if obj.tool_groups_id: - obj.name = obj.functional_name_id.name + item.name = item.functional_name_id.name else: - obj.sudo().name = '' + item.sudo().name = '' @api.constrains('min_stock_num', 'max_stock_num') def _check_stock_num(self): @@ -583,4 +582,10 @@ class RealTimeDistributionOfFunctionalTools(models.Model): for vals in vals_list: vals['status_create'] = False records = super(RealTimeDistributionOfFunctionalTools, self).create(vals_list) + for item in records: + if item: + record = self.search([('functional_name_id', '=', item.functional_name_id.id)]) + if len(record) > 1: + raise ValidationError( + '功能刀具名称为【%s】的安全库存已经存在,请勿重复创建!!!' % item.functional_name_id.name) return records diff --git a/sf_tool_management/models/functional_tool_enroll.py b/sf_tool_management/models/functional_tool_enroll.py index 9c5b1417..ba3553e1 100644 --- a/sf_tool_management/models/functional_tool_enroll.py +++ b/sf_tool_management/models/functional_tool_enroll.py @@ -37,14 +37,10 @@ class ToolDatasync(models.Model): def _cron_tool_datasync_all(self): try: - self.env['stock.lot'].sudo().sync_enroll_tool_material_stock_all() - - self.env['stock.lot'].sudo().sync_enroll_fixture_material_stock_all() - self.env['sf.tool.material.search'].sudo().sync_enroll_tool_material_all() - + self.env['stock.lot'].sudo().sync_enroll_tool_material_stock_all() self.env['sf.fixture.material.search'].sudo().sync_enroll_fixture_material_all() - + self.env['stock.lot'].sudo().sync_enroll_fixture_material_stock_all() self.env['sf.functional.cutting.tool.entity'].sudo().esync_enroll_functional_tool_entity_all() logging.info("已全部同步完成!!!") # self.env['sf.functional.tool.warning'].sudo().sync_enroll_functional_tool_warning_all() @@ -106,7 +102,7 @@ class StockLot(models.Model): logging.info("没有刀具物料序列号信息") except Exception as e: logging.info("刀具物料序列号同步失败:%s" % e) - + class ToolMaterial(models.Model): _inherit = 'sf.tool.material.search' @@ -198,7 +194,7 @@ class FunctionalCuttingToolEntity(models.Model): for item in objs_all: val = { 'id': item.id, - 'code': item.code, + 'code': False if not item.code else item.code.split('-', 1)[1], 'name': item.name, 'rfid': item.rfid, 'tool_groups_name': item.tool_groups_id.name, diff --git a/sf_tool_management/views/functional_tool_views.xml b/sf_tool_management/views/functional_tool_views.xml index 6fde82a0..103583c2 100644 --- a/sf_tool_management/views/functional_tool_views.xml +++ b/sf_tool_management/views/functional_tool_views.xml @@ -29,7 +29,6 @@ - @@ -61,13 +60,9 @@ +

@@ -711,10 +723,14 @@ - - - - + + + + @@ -759,10 +775,25 @@
+
+ + +

@@ -773,12 +804,13 @@ - - + + + @@ -791,8 +823,8 @@ attrs="{'readonly': [('state', '=', '已拆解')]}"/> - + + @@ -879,6 +911,23 @@ + + + + + + + + + + + + + + + diff --git a/sf_tool_management/wizard/wizard.py b/sf_tool_management/wizard/wizard.py index af772016..4edd3617 100644 --- a/sf_tool_management/wizard/wizard.py +++ b/sf_tool_management/wizard/wizard.py @@ -621,26 +621,10 @@ class FunctionalToolAssemblyOrder(models.TransientModel): desc_1 = self.get_desc_1(stock_lot) # 封装功能刀具数据,用于创建功能刀具记录 desc_2 = self.get_desc_2(stock_lot, functional_tool_assembly) - # 创建刀具组装入库单 - self.env['stock.picking'].create_stocking_picking(stock_lot, functional_tool_assembly, self) - # 刀具物料出库 - if self.handle_code_id: - product_id.tool_material_stock_moves(self.handle_code_id, self.assembly_order_code) - if self.integral_product_id: - self.integral_product_id.material_stock_moves(self.integral_freight_barcode_id, - self.integral_freight_lot_id, self.assembly_order_code) - if self.blade_product_id: - self.blade_product_id.material_stock_moves(self.blade_freight_barcode_id, - self.blade_freight_lot_id, self.assembly_order_code) - if self.bar_product_id: - self.bar_product_id.material_stock_moves(self.bar_freight_barcode_id, - self.bar_freight_lot_id, self.assembly_order_code) - if self.pad_product_id: - self.pad_product_id.material_stock_moves(self.pad_freight_barcode_id, - self.pad_freight_lot_id, self.assembly_order_code) - if self.chuck_product_id: - self.chuck_product_id.material_stock_moves(self.chuck_freight_barcode_id, - self.chuck_freight_lot_id, self.assembly_order_code) + # 创建功能刀具组装入库单 + self.env['stock.picking'].create_tool_stocking_picking(stock_lot, functional_tool_assembly, self) + # 创建刀具物料出库单 + self.env['stock.picking'].create_tool_stocking_picking1(self) # ============================创建功能刀具列表、安全库存记录=============================== # 创建功能刀具列表记录 @@ -786,9 +770,9 @@ class FunctionalToolAssemblyOrder(models.TransientModel): class StockPicking(models.Model): _inherit = 'stock.picking' - def create_stocking_picking(self, stock_lot, functional_tool_assembly, obj): + def create_tool_stocking_picking(self, stock_lot, functional_tool_assembly, obj): """ - 创建刀具组装入库单 + 创建功能刀具组装入库单 """ # 获取名称为刀具组装入库的作业类型 picking_type_id = self.env['stock.picking.type'].sudo().search([('name', '=', '刀具组装入库')]) @@ -807,6 +791,7 @@ class StockPicking(models.Model): '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': functional_tool_assembly.id, 'functional_tool_type_id': obj.functional_tool_type_id.id, @@ -836,6 +821,101 @@ class StockPicking(models.Model): num = "%03d" % m return name + str(num) + 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: + datas['data'].append( + {'current_location_id': self.env['sf.shelf.location'], 'lot_id': obj.handle_code_id}) + if obj.integral_product_id: + datas['data'].append( + {'current_location_id': obj.integral_freight_barcode_id, 'lot_id': obj.integral_freight_lot_id.lot_id}) + if obj.blade_product_id: + datas['data'].append( + {'current_location_id': obj.blade_freight_barcode_id, 'lot_id': obj.blade_freight_lot_id.lot_id}) + if obj.bar_product_id: + datas['data'].append( + {'current_location_id': obj.bar_freight_barcode_id, 'lot_id': obj.bar_freight_lot_id.lot_id}) + if obj.pad_product_id: + datas['data'].append( + {'current_location_id': obj.pad_freight_barcode_id, 'lot_id': obj.pad_freight_lot_id.lot_id}) + if obj.chuck_product_id: + datas['data'].append( + {'current_location_id': obj.chuck_freight_barcode_id, 'lot_id': obj.chuck_freight_lot_id.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() + + def _get_name_stock1(self, picking_type_id): + name = picking_type_id.sequence_id.prefix + 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[-3:]) + 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: + # 创建库存移动记录 + 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' @@ -853,13 +933,6 @@ class ProductProduct(models.Model): 'product_id': product_id[0].id, 'company_id': self.env.company.id }) - # 获取位置对象 - location_inventory_ids = self.env['stock.location'].search([('name', 'in', ('Production', '生产'))]) - stock_location_id = self.env['stock.location'].search([('name', '=', '组装后')]) - # 创建功能刀具该批次/序列号 库存移动和移动历史 - stock_lot.create_stock_quant(location_inventory_ids[-1], stock_location_id, functional_tool_assembly.id, - obj.assembly_order_code, obj, obj.after_tool_groups_id) - return stock_lot def get_stock_lot_name(self, obj): @@ -877,87 +950,3 @@ class ProductProduct(models.Model): m = int(stock_lot_id.name[-3:]) + 1 num = "%03d" % m return '%s-%s' % (code, num) - - def tool_material_stock_moves(self, tool_material, assembly_order_code): - """ - 对刀具物料进行库存移动到 刀具组装位置 - """ - # 获取位置对象 - location_inventory_id = tool_material.quant_ids.location_id[-1] - stock_location_id = self.env['stock.location'].search([('name', '=', '刀具组装位置')]) - # 创建功能刀具该批次/序列号 库存移动和移动历史 - tool_material.create_stock_quant(location_inventory_id, stock_location_id, None, assembly_order_code, False, - False) - - def material_stock_moves(self, shelf_location_barcode_id, lot_id, assembly_order_code): - # 创建库存移动记录 - stock_move_id = self.env['stock.move'].sudo().create({ - 'name': assembly_order_code, - 'product_id': self.id, - 'location_id': self.env['stock.location'].search([('name', '=', '刀具房')]).id, - 'location_dest_id': self.env['stock.location'].search([('name', '=', '刀具组装位置')]).id, - 'product_uom_qty': 1.00, - 'state': 'done' - }) - - # 创建移动历史记录 - stock_move_line_id = self.env['stock.move.line'].sudo().create({ - 'product_id': self.id, - 'move_id': stock_move_id.id, - 'lot_id': lot_id.lot_id.id, - 'current_location_id': shelf_location_barcode_id.id, - 'install_tool_time': fields.Datetime.now(), - 'qty_done': 1.0, - 'state': 'done', - }) - return stock_move_id, stock_move_line_id - - -class StockLot(models.Model): - _inherit = 'stock.lot' - - def create_stock_quant(self, location_inventory_id, stock_location_id, functional_tool_assembly_id, name, obj, - tool_groups_id): - """ - 对功能刀具组装过程的功能刀具和刀具物料进行库存移动,以及创建移动历史 - """ - - # 创建库存移动记录 - stock_move_id = self.env['stock.move'].sudo().create({ - 'name': name, - 'product_id': self.product_id.id, - 'location_id': location_inventory_id.id, - 'location_dest_id': stock_location_id.id, - 'product_uom_qty': 1.00, - 'state': 'done' - }) - - # 创建移动历史记录 - stock_move_line_id = self.env['stock.move.line'].sudo().create({ - 'product_id': self.product_id.id, - 'functional_tool_name_id': functional_tool_assembly_id, - 'lot_id': self.id, - 'move_id': stock_move_id.id, - 'install_tool_time': fields.Datetime.now(), - 'qty_done': 1.0, - 'state': 'done', - 'functional_tool_type_id': False if not obj else obj.functional_tool_type_id.id, - 'diameter': None if not obj else obj.after_assembly_functional_tool_diameter, - 'knife_tip_r_angle': None if not obj else obj.after_assembly_knife_tip_r_angle, - 'code': '' if not obj else obj.code, - 'rfid': '' if not obj else obj.rfid, - 'functional_tool_name': '' if not obj else obj.after_assembly_functional_tool_name, - 'tool_groups_id': False if not tool_groups_id else tool_groups_id.id - }) - return stock_move_id, stock_move_line_id - -# class StockQuant(models.Model): -# _inherit = 'stock.quant' -# -# @api.model_create_multi -# def create(self, vals_list): -# records = super(StockQuant, self).create(vals_list) -# for record in records: -# if record.lot_id.product_id.categ_id.name == '刀具': -# record.lot_id.enroll_tool_material_stock() -# return records