Files
test/sf_warehouse/models/model.py
龚启豪 9650f45ba2 Accept Merge Request #714: (feature/修改机床参数bug -> develop)
Merge Request: 库存根据权限增加审核按钮 以及审核状态

Created By: @龚启豪
Reviewed By: @马广威
Approved By: @马广威 
Accepted By: @龚启豪
URL: https://jikimo-hn.coding.net/p/jikimo_sfs/d/jikimo_sf/git/merge/714?initial=true
2023-12-08 15:21:32 +08:00

652 lines
26 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- coding: utf-8 -*-
import logging
from odoo import api, fields, models
from odoo.osv import expression
class SfLocation(models.Model):
_inherit = 'stock.location'
# 重写字段定义
name = fields.Char('Location Name', required=True, size=20)
barcode = fields.Char('Barcode', copy=False, size=15)
check_state = fields.Selection([
('enable', '启用'),
('close', '关闭')
], string='审核状态', default='close')
def action_check(self):
self.check_state = 'enable'
# 仓库类别selection库区、库位、货位
# location_type = fields.Selection([
# ('库区', '库区'),
# ('货架', '货架'),
# ('货位', '货位')
# ], string='存储类型')
location_type = fields.Selection([
('库区', '库区')
], string='存储类型')
# 库区类型selection拣货区、存货区、收货区、退货区、次品区
area_type = fields.Selection([
('拣货区', '拣货区'),
('存货区', '存货区'),
('收货区', '收货区'),
('退货区', '退货区'),
('次品区', '次品区')
], string='库区类型')
# 当前位置
current_location_id = fields.Many2one('sf.shelf.location', string='当前位置')
# 目的位置
destination_location_id = fields.Many2one('sf.shelf.location', string='目的位置')
# 存储类型selection库区、货架
# storage_type = fields.Selection([
# ('库区', '库区'),
# ('货架', '货架')
# ], string='存储类型')
# 产品类别 关联product.category
product_type = fields.Many2many('product.category', string='产品类别')
# 货架独有字段通道、方向、货架高度m、货架层数、层数容量
channel = fields.Char(string='通道')
direction = fields.Selection([
('R', 'R'),
('L', 'L')
], string='方向')
shelf_height = fields.Float(string='货架高度(m)')
shelf_layer = fields.Integer(string='货架层数')
layer_capacity = fields.Integer(string='层数容量')
# 货位独有字段:货位状态、产品(关联产品对象)、产品序列号(关联产品序列号对象)
location_status = fields.Selection([
('空闲', '空闲'),
('占用', '占用'),
('禁用', '禁用')
], string='货位状态', default='空闲')
# product_id = fields.Many2one('product.template', string='产品')
product_id = fields.Many2one('product.product', string='产品', compute='_compute_product_id', readonly=True)
product_sn_id = fields.Many2one('stock.lot', string='产品序列号')
# time_test = fields.Char(string='time')
# 添加SQL约束
# _sql_constraints = [
# ('name_uniq', 'unique(name)', '位置名称必须唯一!'),
# ]
hide_location_type = fields.Boolean(compute='_compute_hide_what', string='隐藏仓库')
hide_area = fields.Boolean(compute='_compute_hide_what', string='隐藏库区')
hide_shelf = fields.Boolean(compute='_compute_hide_what', string='隐藏货架')
hide_location = fields.Boolean(compute='_compute_hide_what', string='隐藏货位')
# @api.model
# def create(self, vals):
# """
# 重写create方法添加自定义的约束
# """
# print('create', vals)
# if vals.get('location_id'):
# location = self.env['stock.location'].browse(vals.get('location_id'))
# if location.storage_type == '库区':
# raise UserError('库区不能作为父级仓库')
# return super().create(vals)
#
# @api.onchange('location_id')
# def _onchange_location_id(self):
# """
# 重写onchange方法添加自定义的约束
# """
# if self.location_id:
# if self.location_id.storage_type == '库区':
# raise UserError('库区不能作为父级仓库')
# @api.constrains('shelf_height')
# def _check_shelf_height(self):
# for record in self:
# if not (0 <= record.shelf_height < 1000): # 限制字段值在0到999之间
# raise UserError('shelf_height的值必须在0到1000之间')
#
# @api.constrains('shelf_layer')
# def _check_shelf_layer(self):
# for record in self:
# if not (0 < record.shelf_layer < 1000):
# raise UserError('shelf_layer的值必须在0到999之间,且不能为0')
#
# @api.constrains('layer_capacity')
# def _check_layer_capacity(self):
# for record in self:
# if not (0 <= record.layer_capacity < 1000):
# raise UserError('layer_capacity的值必须在0到999之间,且不能为0')
@api.depends('product_sn_id')
def _compute_product_id(self):
"""
根据产品序列号,获取产品
"""
for record in self:
if record.product_sn_id:
record.product_id = record.product_sn_id.product_id
record.location_status = '占用'
else:
record.product_id = False
# record.location_status = '空闲'
@api.depends('location_type')
def _compute_hide_what(self):
"""
根据仓库类别,隐藏不需要的字段
:return:
"""
for record in self:
record.hide_location_type = False
record.hide_area = False
record.hide_shelf = False
record.hide_location = False
if record.location_type and record.location_type == '仓库':
record.hide_location_type = True
elif record.location_type and record.location_type == '库区':
record.hide_area = True
elif record.location_type and record.location_type == '货架':
record.hide_shelf = True
elif record.location_type and record.location_type == '货位':
record.hide_location = True
else:
pass
# # 添加Python约束
# @api.constrains('name', 'barcode')
# def _check_len(self):
# for rec in self:
# if len(rec.name) > 20:
# raise ValidationError("Location Name length must be less equal than 20!")
# if len(rec.barcode) > 15:
# raise ValidationError("Barcode length must be less equal than 15!")
# @api.model
# def default_get(self, fields):
# print('fields:', fields)
# res = super(SfLocation, self).default_get(fields)
# print('res:', res)
# if 'barcode' in fields and 'barcode' not in res:
# # 这里是你生成barcode的代码
# pass
# # res['barcode'] = self.generate_barcode() # 假设你有一个方法generate_barcode来生成barcode
# return res
# @api.model
# def create(self, vals):
# """
# 重写create方法当仓库类型为货架时自动生成其下面的货位数量为货架层数*层数容量
# """
# res = super(SfLocation, self).create(vals)
# if res.location_type == '货架':
# for i in range(res.shelf_layer):
# for j in range(res.layer_capacity):
# self.create({
# 'name': res.name + '-' + str(i+1) + '-' + str(j+1),
# 'location_id': res.id,
# 'location_type': '货位',
# 'barcode': self.generate_barcode(res, i, j),
# 'location_status': '空闲'
# })
# return res
# 生成货位
def create_location(self):
"""
当仓库类型为货架时,自动生成其下面的货位,数量为货架层数*层数容量
"""
pass
# if self.location_type == '货架':
# for i in range(self.shelf_layer):
# for j in range(self.layer_capacity):
# self.create({
# 'name': self.name + '-' + str(i + 1) + '层' + '-' + str(j + 1) + '位置',
# 'location_id': self.id,
# 'location_type': '货位',
# 'barcode': self.generate_barcode(i, j),
# 'location_status': '空闲'
# })
def generate_barcode(self, i, j):
"""
生成货位条码
"""
pass
# # 这里是你生成barcode的代码
# # area_type_barcode = self.location_id.barcode
# area_type_barcode = self.barcode
# i_str = str(i + 1).zfill(3) # 确保是两位数如果不足两位左侧补0
# j_str = str(j + 1).zfill(3) # 确保是两位数如果不足两位左侧补0
# return area_type_barcode + self.channel + self.direction + '-' + self.barcode + '-' + i_str + '-' + j_str
class ShelfLocation(models.Model):
_name = 'sf.shelf.location'
_description = '货架货位'
_order = 'name'
name = fields.Char('名称', required=True, size=20)
barcode = fields.Char('编码', copy=False, size=15)
check_state = fields.Selection([
('enable', '启用'),
('close', '关闭')
], string='审核状态', default='close')
def action_check(self):
self.check_state = 'enable'
# 仓库类别selection库区、库位、货位
location_type = fields.Selection([
('货架', '货架'),
('货位', '货位')
], string='存储类型')
# 绑定库区
shelf_location_id = fields.Many2one('stock.location', string='所属库区', domain=[('location_type', '=', '库区')])
location_id = fields.Many2one('stock.location', string='所属库区', domain=[('location_type', '=', '库区')])
# 产品类别 关联product.category
# product_type = fields.Many2many('product.category', string='产品类别')
# picking_product_type = fields.Many2many('stock.picking', string='调拨产品类别', related='location_dest_id.product_type')
# 货架独有字段通道、方向、货架高度m、货架层数、层数容量
channel = fields.Char(string='通道')
direction = fields.Selection([
('R', 'R'),
('L', 'L')
], string='方向')
shelf_height = fields.Float(string='货架高度(m)')
shelf_layer = fields.Integer(string='货架层数')
layer_capacity = fields.Integer(string='层数容量')
# 货位独有字段:货位状态、产品(关联产品对象)、产品序列号(关联产品序列号对象)
location_status = fields.Selection([
('空闲', '空闲'),
('占用', '占用'),
('禁用', '禁用')
], string='货位状态', default='空闲')
# product_id = fields.Many2one('product.template', string='产品')
product_id = fields.Many2one('product.product', string='产品', compute='_compute_product_id', readonly=True)
product_sn_id = fields.Many2one('stock.lot', string='产品序列号')
hide_shelf = fields.Boolean(compute='_compute_hide_what', string='隐藏货架')
hide_location = fields.Boolean(compute='_compute_hide_what', string='隐藏货位')
@api.onchange('shelf_location_id')
def _onchange_shelf_location_id(self):
"""
根据货架的所属库区修改货位的所属库区
"""
if self.name:
all_location = self.env['sf.shelf.location'].search([('name', 'ilike', self.name)])
for record in self:
for location in all_location:
location.location_id = record.shelf_location_id.id
@api.depends('product_sn_id')
def _compute_product_id(self):
"""
根据产品序列号,获取产品
"""
for record in self:
if record.product_sn_id:
record.product_id = record.product_sn_id.product_id
record.location_status = '占用'
else:
record.product_id = False
record.location_status = '空闲'
@api.depends('location_type')
def _compute_hide_what(self):
"""
根据仓库类别,隐藏不需要的字段
:return:
"""
for record in self:
record.hide_shelf = False
record.hide_location = False
if record.location_type and record.location_type == '货架':
record.hide_shelf = True
elif record.location_type and record.location_type == '货位':
record.hide_location = True
else:
pass
def create_location(self):
"""
当仓库类型为货架时,自动生成其下面的货位,数量为货架层数*层数容量
"""
if self.location_type == '货架':
for i in range(self.shelf_layer):
for j in range(self.layer_capacity):
location_name = self.name + '-' + str(i + 1) + '' + '-' + str(j + 1) + '位置'
# 检查是否已经有同名的位置存在
existing_location = self.search([('name', '=', location_name)])
if not existing_location:
self.create({
'name': location_name,
'location_id': self.shelf_location_id.id,
'location_type': '货位',
'barcode': self.generate_barcode(i, j),
'location_status': '空闲',
})
def generate_barcode(self, i, j):
"""
生成货位条码
"""
# 这里是你生成barcode的代码
# area_type_barcode = self.location_id.barcode
area_type_barcode = self.barcode
i_str = str(i + 1).zfill(3) # 确保是两位数如果不足两位左侧补0
j_str = str(j + 1).zfill(3) # 确保是两位数如果不足两位左侧补0
return area_type_barcode + self.channel + self.direction + '-' + self.barcode + '-' + i_str + '-' + j_str
class Sf_stock_move_line(models.Model):
_inherit = 'stock.move.line'
current_location_id = fields.Many2one(
'sf.shelf.location', string='当前货位', compute='_compute_current_location_id', store=True)
# location_dest_id = fields.Many2one('stock.location', string='目标库位')
location_dest_id_product_type = fields.Many2many(related='location_dest_id.product_type')
location_dest_id_value = fields.Integer(compute='_compute_location_dest_id_value', store=True)
# def button_test(self):
# print(self.picking_id.name)
# stock_picking = self.env['stock.picking'].search([('name', '=', self.picking_id.name)], limit=1)
# print(self.picking_id.name)
# print(aa.move_line_ids.lot_id.name)
# # 获取当前的stock.picking对象
# current_picking = self.env['stock.picking'].search([('name', '=', self.picking_id.name)], limit=1)
#
# # 获取当前picking的第一个stock.move对象
# current_move = current_picking.move_ids[0] if current_picking.move_ids else False
#
# # 如果存在相关的stock.move对象
# if current_move:
# # 获取源stock.move对象
# origin_move = current_move.move_orig_ids[0] if current_move.move_orig_ids else False
#
# # 从源stock.move对象获取源stock.picking对象
# origin_picking = origin_move.picking_id if origin_move else False
# # 现在origin_picking就是current_picking的上一步
# # 获取目标stock.move对象
# dest_move = current_move.move_dest_ids[0] if current_move.move_dest_ids else False
#
# # 从目标stock.move对象获取目标stock.picking对象
# dest_picking = dest_move.picking_id if dest_move else False
# # 现在dest_picking就是current_picking的下一步
@api.depends('location_id')
def _compute_current_location_id(self):
for record in self:
# 使用record代替self来引用当前遍历到的记录
logging.info('record.picking_id.name: %s' % record.picking_id.name)
logging.info('record.env: %s' % record.env['stock.picking'].search([('name', '=', record.picking_id.name)]))
# 获取当前的stock.picking对象
current_picking = record.env['stock.picking'].search([('name', '=', record.picking_id.name)], limit=1)
# 获取当前picking的第一个stock.move对象
current_move = current_picking.move_ids[0] if current_picking.move_ids else False
# 如果存在相关的stock.move对象
if current_move:
# 获取源stock.move对象
origin_move = current_move.move_orig_ids[0] if current_move.move_orig_ids else False
# 从源stock.move对象获取源stock.picking对象
origin_picking = origin_move.picking_id if origin_move else False
# 如果前一个调拨单有目标货位
if origin_picking:
for i in current_picking.move_line_ids:
for j in origin_picking.move_line_ids:
if j.destination_location_id and i.lot_id == j.lot_id:
# 更新当前记录的current_location_id字段
record.current_location_id = j.destination_location_id
# # 获取目标stock.move对象
# dest_move = current_move.move_dest_ids[0] if current_move.move_dest_ids else False
#
# # 从目标stock.move对象获取目标stock.picking对象
# dest_picking = dest_move.picking_id if dest_move else False
# # 现在dest_picking就是current_picking的下一步
# 是一张单据一张单据往下走的,所以这里的目标货位是上一张单据的当前货位,且这样去计算是可以的。
@api.depends('location_dest_id')
def _compute_location_dest_id_value(self):
for record in self:
record.location_dest_id_value = record.location_dest_id.id if record.location_dest_id else False
destination_location_id = fields.Many2one(
'sf.shelf.location', string='目标货位')
@api.onchange('destination_location_id')
def _compute_destination_location_id(self):
for record in self:
shelf_location_obj = self.env['sf.shelf.location'].search(
[('product_sn_id', '=', record.lot_id.id)])
if shelf_location_obj:
shelf_location_obj.product_sn_id = False
# obj = self.env['sf.shelf.location'].search([('location_id', '=',
# self.destination_location_id.id)])
obj = self.env['sf.shelf.location'].search([('name', '=',
self.destination_location_id.name)])
if obj:
obj.product_sn_id = record.lot_id.id
else:
pass
else:
obj = self.env['sf.shelf.location'].search([('name', '=',
self.destination_location_id.name)])
if obj:
obj.product_sn_id = record.lot_id.id
class SfStockPicking(models.Model):
_inherit = 'stock.picking'
def button_validate(self):
"""
重写验证方法,当验证时意味着调拨单已经完成,已经移动到了目标货位,所以需要将当前货位的状态改为空闲
"""
res = super(SfStockPicking, self).button_validate()
for line in self.move_line_ids:
if line:
if line.current_location_id:
line.current_location_id.product_sn_id = False
line.current_location_id = False
return res
class SfProcurementGroup(models.Model):
_inherit = 'procurement.group'
@api.model
def _search_rule(self, route_ids, packaging_id, product_id, warehouse_id, domain):
"""
修改路线多规则条件选取
"""
if warehouse_id:
domain = expression.AND(
[['|', ('warehouse_id', '=', warehouse_id.id), ('warehouse_id', '=', False)], domain])
Rule = self.env['stock.rule']
res = self.env['stock.rule']
if route_ids:
res_list = Rule.search(expression.AND([[('route_id', 'in', route_ids.ids)], domain]),
order='route_sequence, sequence')
for res1 in res_list:
if product_id.categ_id in res1.location_dest_id.product_type or product_id.categ_id in \
res1.location_src_id.product_type:
res = res1
if not res:
res = Rule.search(expression.AND([[('route_id', 'in', route_ids.ids)], domain]),
order='route_sequence, sequence', limit=1)
if not res and packaging_id:
packaging_routes = packaging_id.route_ids
if packaging_routes:
res_list = Rule.search(expression.AND([[('route_id', 'in', packaging_routes.ids)], domain]),
order='route_sequence, sequence')
for res1 in res_list:
if product_id.categ_id in res1.location_dest_id.product_type or product_id.categ_id in \
res1.location_src_id.product_type:
res = res1
if not res:
res = Rule.search(expression.AND([[('route_id', 'in', packaging_routes.ids)], domain]),
order='route_sequence, sequence', limit=1)
if not res:
product_routes = product_id.route_ids | product_id.categ_id.total_route_ids
if product_routes:
res_list = Rule.search(expression.AND([[('route_id', 'in', product_routes.ids)], domain]),
order='route_sequence, sequence')
for res1 in res_list:
if product_id.categ_id in res1.location_dest_id.product_type or product_id.categ_id in \
res1.location_src_id.product_type:
res = res1
if not res:
res = Rule.search(expression.AND([[('route_id', 'in', product_routes.ids)], domain]),
order='route_sequence, sequence', limit=1)
if not res and warehouse_id:
warehouse_routes = warehouse_id.route_ids
if warehouse_routes:
res_list = Rule.search(expression.AND([[('route_id', 'in', warehouse_routes.ids)], domain]),
order='route_sequence, sequence')
for res1 in res_list:
if product_id.categ_id in res1.location_dest_id.product_type or product_id.categ_id in \
res1.location_src_id.product_type:
res = res1
if not res:
res = Rule.search(expression.AND([[('route_id', 'in', warehouse_routes.ids)], domain]),
order='route_sequence, sequence', limit=1)
return res
class SfWarehouse(models.Model):
_inherit = 'stock.warehouse'
check_state = fields.Selection([
('enable', '启用'),
('close', '关闭')
], string='审核状态', default='close')
def action_check(self):
self.check_state = 'enable'
class SfRule(models.Model):
_inherit = 'stock.rule'
check_state = fields.Selection([
('enable', '启用'),
('close', '关闭')
], string='审核状态', default='close')
def action_check(self):
self.check_state = 'enable'
class SfRoute(models.Model):
_inherit = 'stock.route'
check_state = fields.Selection([
('enable', '启用'),
('close', '关闭')
], string='审核状态', default='close')
def action_check(self):
self.check_state = 'enable'
class SfPickingType(models.Model):
_inherit = 'stock.picking.type'
check_state = fields.Selection([
('enable', '启用'),
('close', '关闭')
], string='审核状态', default='close')
def action_check(self):
self.check_state = 'enable'
class SfProductCategory(models.Model):
_inherit = 'product.category'
check_state = fields.Selection([
('enable', '启用'),
('close', '关闭')
], string='审核状态', default='close')
def action_check(self):
self.check_state = 'enable'
class SfUomCategory(models.Model):
_inherit = 'uom.category'
check_state = fields.Selection([
('enable', '启用'),
('close', '关闭')
], string='审核状态', default='close')
def action_check(self):
self.check_state = 'enable'
class SfBarcodeNomenclature(models.Model):
_inherit = 'barcode.nomenclature'
check_state = fields.Selection([
('enable', '启用'),
('close', '关闭')
], string='审核状态', default='close')
def action_check(self):
self.check_state = 'enable'
class SfPutawayRule(models.Model):
_inherit = 'stock.putaway.rule'
check_state = fields.Selection([
('enable', '同意'),
('close', '不同意')
], string='审核状态', default='close')
def action_check(self):
self.check_state = 'enable'
class SfWarehouseOrderpoint(models.Model):
_inherit = 'stock.warehouse.orderpoint'
check_state = fields.Selection([
('enable', '同意'),
('close', '不同意')
], string='审核状态', default='close')
def action_check(self):
self.check_state = 'enable'
class SfStockQuant(models.Model):
_inherit = 'stock.quant'
check_state = fields.Selection([
('enable', '同意'),
('close', '不同意')
], string='审核状态', default='close')
def action_check(self):
self.check_state = 'enable'
class SfStockScrap(models.Model):
_inherit = 'stock.scrap'
check_state = fields.Selection([
('enable', '启用'),
('close', '关闭')
], string='审核状态', default='close')
def action_check(self):
self.check_state = 'enable'