Accept Merge Request #1141: (feature/功能刀具拆解单库存优化 -> develop)

Merge Request: 1、功能刀具组装/拆解单库存优化;2、完成 SF材料材料型号缺陷优化 bug

Created By: @禹翔辉
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @禹翔辉
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/1141
This commit is contained in:
禹翔辉
2024-07-08 09:57:04 +08:00
committed by Coding
10 changed files with 440 additions and 255 deletions

View File

@@ -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("请添加供应商")

View File

@@ -251,7 +251,7 @@
<group>
<group>
<field name="materials_no" readonly="1" force_save="1"/>
<field name="gain_way" required="1"/>
<field name="gain_way" required="0"/>
<field name="tensile_strength" required="1"/>
<field name="hardness" required="1"/>
<field name="density" readonly="1"/>
@@ -270,9 +270,9 @@
<notebook>
<page string="供应商">
<field name='supplier_ids' class="supplier_ids_set_css">
<tree editable='bottom'>
<tree editable='bottom' delete="1">
<field name="sequence" widget="handle" string="序号"/>
<field name="partner_id" string="名称"/>
<field name="partner_id" string="名称" options="{'no_create': True}"/>
</tree>
</field>
</page>

View File

@@ -22,6 +22,16 @@
<field name="company_id" ref="base.main_company"/>
</record>
<record id="stock_location_tool_dismantle" model="stock.location">
<field name="name">拆解</field>
<field name="location_id" ref="stock.stock_location_locations_virtual"/>
<field name="usage">internal</field>
<field name="barcode">DJCJ</field>
<field name="scrap_location">true</field>
<field name="active">true</field>
<field name="company_id" ref="base.main_company"/>
</record>
<record id="after_assembly_picking_in" model="stock.picking.type">
<field name="name">刀具组装入库</field>

View File

@@ -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):

View File

@@ -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

View File

@@ -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

View File

@@ -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,

View File

@@ -29,7 +29,6 @@
<field name="current_location" string="当前位置"/>
<field name="current_location_id" invisible="1"/>
<field name="current_location" optional="hide"/>
<field name="sf_cutting_tool_type_id" invisible="True"/>
</tree>
</field>
@@ -61,13 +60,9 @@
</button>
<button class="oe_stat_button" groups="sf_base.group_sf_mrp_user"
name="open_stock_move_line"
icon="fa-list-ul"
type="object">
<div class="o_field_widget o_stat_info">
<span>
出库入库记录
</span>
</div>
<i class="fa fa-fw o_button_icon fa-exchange"/>
<span>出库入库记录</span>
</button>
<button class="oe_stat_button" groups="sf_base.group_sf_mrp_user"
name="open_safety_stock"
@@ -174,7 +169,7 @@
<field name="cut_time" attrs="{'invisible': [('new_former','=','0')]}"/>
<field name="cut_length" attrs="{'invisible': [('new_former','=','0')]}"/>
<field name="cut_number" attrs="{'invisible': [('new_former','=','0')]}"/>
<field name="current_location_id" string="当前位置"/>
<field name="current_location_id" string="当前位置" invisible="1"/>
<field name="current_location" string="当前位置"/>
<field name="current_shelf_location_id" string="当前货位"
attrs="{'invisible': [('current_shelf_location_id', '=', False)]}"/>
@@ -482,18 +477,19 @@
<field name="reference" string="单据号"/>
<field name="lot_id" invisible="1"/>
<field name="rfid"/>
<field name="functional_tool_name_id" optional="hide"/>
<field name="functional_tool_name" string="功能刀具名称"/>
<field name="diameter"/>
<field name="knife_tip_r_angle"/>
<field name="install_tool_time"/>
<field name="location_id"/>
<field name="current_location_id"/>
<field name="location_dest_id"/>
<field name="destination_location_id"/>
<field name="date"/>
<field name="qty_done" string="数量"/>
<field name="functional_tool_type_id" invisible="True"/>
<field name="functional_tool_type_id" invisible="1"/>
<field name="functional_tool_name_id" invisible="1"/>
<field name="functional_tool_dismantle_id" invisible="1"/>
<field name="install_tool_time" invisible="1"/>
<!-- <button name="enroll_functional_tool_move" string="安全库存注册" type="object" class="btn-primary"/>-->
</tree>
</field>

View File

@@ -407,6 +407,7 @@
<field name="plan_execute_status"/>
<filter string="已归档" name="inactive" domain="[('active', '=', False)]"/>
<searchpanel>
<field name="plan_execute_status" string="状态" enable_counters="1" icon="fa-filter"/>
<field name="production_line_id" string="生产线" enable_counters="1" icon="fa-filter"/>
<field name="functional_tool_type_id" string="功能刀具类型" enable_counters="1"
icon="fa-filter"/>
@@ -476,6 +477,17 @@
<field name="assemble_status" widget="statusbar" statusbar_visible="0,1"/>
</header>
<sheet>
<div class="oe_button_box" name="button_box">
<button class="oe_stat_button" name="open_tool_stock_picking" icon="fa-truck" type="object"
attrs="{'invisible': [('assemble_status', '!=', '1')]}">
<div name="delivery_count" class="o_field_widget o_readonly_modifier o_field_statinfo">
<span class="o_stat_info o_stat_value">
<field name="picking_num"/>
</span>
<span class="o_stat_text">调拨</span>
</div>
</button>
</div>
<div class="oe_title">
<h1>
<field name="assembly_order_code"/>
@@ -711,10 +723,14 @@
</searchpanel>
<group expand="0" string="Group By...">
<filter string="功能刀具名称" name="name" domain="[]" context="{'group_by': 'functional_tool_name'}"/>
<filter string="刀具组" name="tool_groups" domain="[]" context="{'group_by': 'tool_groups_id'}"/>
<filter string="任务来源" name="loading_task_source" domain="[]" context="{'group_by': 'loading_task_source'}"/>
<filter string="用刀时间" name="use_tool_time" domain="[]" context="{'group_by': 'use_tool_time'}"/>
<filter string="功能刀具名称" name="name" domain="[]"
context="{'group_by': 'functional_tool_name'}"/>
<filter string="刀具组" name="tool_groups" domain="[]"
context="{'group_by': 'tool_groups_id'}"/>
<filter string="任务来源" name="loading_task_source" domain="[]"
context="{'group_by': 'loading_task_source'}"/>
<filter string="用刀时间" name="use_tool_time" domain="[]"
context="{'group_by': 'use_tool_time'}"/>
</group>
</search>
</field>
@@ -759,10 +775,25 @@
<form>
<header>
<button string="确认拆解" name="confirmation_disassembly" type="object" class="btn-primary"
confirm="是否确认拆解" attrs="{'invisible': [('state', '=', '拆解')]}"/>
confirm="是否确认拆解" attrs="{'invisible': [('state', '!=', '拆解')]}"/>
<field name="state" widget="statusbar" statusbar_visible="待拆解,已拆解"/>
</header>
<sheet>
<div class="oe_button_box" name="button_box">
<button class="oe_stat_button" name="open_tool_stock_picking" icon="fa-truck" type="object"
attrs="{'invisible':[('state', '!=', '已拆解')]}">
<div name="delivery_count" class="o_field_widget o_readonly_modifier o_field_statinfo">
<span class="o_stat_info o_stat_value">
<field name="picking_num"/>
</span>
<span class="o_stat_text">调拨</span>
</div>
</button>
<button class="oe_stat_button" name="open_function_tool_stock_move_line" icon="fa-exchange"
type="object">
<span>功能刀具移动</span>
</button>
</div>
<div class="oe_title">
<h1>
<field name="code"/>
@@ -773,12 +804,13 @@
<group>
<field name="functional_tool_id" placeholder="请选择将要拆解的功能刀具"
options="{'no_create': True}" attrs="{'readonly': [('state', '=', '已拆解')]}"/>
<field name="rfid" attrs="{'invisible': [('state', '=', '已拆解')]}"/>
<field name="rfid_dismantle" attrs="{'invisible': [('state', '!=', '已拆解')]}"/>
<field name="rfid" attrs="{'invisible': [('rfid', '=', '')]}"/>
<field name="rfid_dismantle" attrs="{'invisible': [('rfid_dismantle', '=', False)]}"/>
<field name="tool_type_id"/>
<field name="tool_groups_id"/>
<field name="diameter"/>
<field name="knife_tip_r_angle"/>
<field name="picking_id" invisible="1"/>
</group>
<group>
<field name="image"/>
@@ -791,8 +823,8 @@
attrs="{'readonly': [('state', '=', '已拆解')]}"/>
</group>
<group>
<field name="scrap_id"
attrs="{'invisible': [('dismantle_cause', 'not in', ['寿命到期报废','崩刀报废'])]}"/>
<!-- <field name="scrap_id"-->
<!-- attrs="{'invisible': [('dismantle_cause', 'not in', ['寿命到期报废','崩刀报废'])]}"/>-->
<field name="grinding_id"
attrs="{'invisible': [('dismantle_cause', 'not in', ['刀具需磨削'])]}"/>
</group>
@@ -879,6 +911,23 @@
</group>
</group>
</page>
<page string="报废"
attrs="{'invisible':[('dismantle_cause', 'not in', ['寿命到期报废','崩刀报废'])]}">
<field name="scrap_ids">
<tree>
<field name="name"/>
<field name="product_id"/>
<field name="lot_id"/>
<field name="location_id"/>
<field name="scrap_location_id"/>
<field name="scrap_qty"/>
<field name="product_uom_id"/>
<field name="date_done"/>
<field name="state" widget="badge" decoration-success="state == 'done'"
decoration-muted="state == 'draft'"/>
</tree>
</field>
</page>
<page string="其他">
<group>
<group>

View File

@@ -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