@@ -329,37 +332,46 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sf_warehouse/__init__.py b/sf_warehouse/__init__.py
index c081ee06..c9ba0265 100644
--- a/sf_warehouse/__init__.py
+++ b/sf_warehouse/__init__.py
@@ -1,2 +1,3 @@
# -*-coding:utf-8-*-
from . import models
+from . import wizard
diff --git a/sf_warehouse/__manifest__.py b/sf_warehouse/__manifest__.py
index 04051b06..b409c7ce 100644
--- a/sf_warehouse/__manifest__.py
+++ b/sf_warehouse/__manifest__.py
@@ -15,6 +15,7 @@
'data/ir_cron_data.xml',
'security/sf_stock_security.xml',
'security/ir.model.access.csv',
+ 'wizard/wizard_view.xml',
'views/view.xml',
'views/shelf_location.xml',
'views/change_stock_move_views.xml',
diff --git a/sf_warehouse/models/model.py b/sf_warehouse/models/model.py
index 37030471..50469284 100644
--- a/sf_warehouse/models/model.py
+++ b/sf_warehouse/models/model.py
@@ -191,6 +191,7 @@ class SfLocation(models.Model):
# return res
# 生成货位
+
def create_location(self):
"""
当仓库类型为货架时,自动生成其下面的货位,数量为货架层数*层数容量
@@ -222,11 +223,14 @@ class SfLocation(models.Model):
class SfShelf(models.Model):
_name = 'sf.shelf'
+ _inherit = ['printing.utils']
_description = '货架'
_order = 'create_date desc'
name = fields.Char('货架名称', required=True, size=20)
+ active = fields.Boolean("有效", default=True)
barcode = fields.Char('编码', copy=False, size=15, required=True)
+
# 货位
location_ids = fields.One2many('sf.shelf.location', 'shelf_id', string='货位')
@@ -300,10 +304,40 @@ class SfShelf(models.Model):
j_str = str(j + 1).zfill(3) # 确保是两位数,如果不足两位,左侧补0
return area_type_barcode + self.channel + self.direction + '-' + self.barcode + '-' + i_str + '-' + j_str
+ def print_all_location_barcode(self):
+ """
+ 打印所有货位编码
+ """
+ print('=======打印货架所有货位编码=========')
+ for record in self.location_ids:
+ print('record', record)
+ if not record.barcode:
+ continue
+ record.ensure_one()
+ # qr_code_data = record.lot_qr_code
+ # if not qr_code_data:
+ # raise UserError("没有找到二维码数据。")
+ barcode = record.barcode
+ # todo 待控制
+ if not barcode:
+ raise ValidationError("请先分配序列号")
+ # host = "192.168.50.110" # 可以根据实际情况修改
+ # port = 9100 # 可以根据实际情况修改
+
+ # 获取默认打印机配置
+ printer_config = self.env['printer.configuration'].sudo().search([('model', '=', self._name)], limit=1)
+ if not printer_config:
+ raise UserError('请先配置打印机')
+ host = printer_config.printer_id.ip_address
+ port = printer_config.printer_id.port
+ self.print_qr_code(barcode, host, port)
+
class ShelfLocation(models.Model):
_name = 'sf.shelf.location'
+ _inherit = ['printing.utils']
_description = '货位'
+ _rec_name = 'barcode'
_order = 'id asc, create_date asc'
# current_location_id = fields.Many2one('sf.shelf.location', string='当前位置')
@@ -313,6 +347,7 @@ class ShelfLocation(models.Model):
destination_move_ids = fields.One2many('stock.move.line', 'destination_location_id', '目标位置调拨单')
storage_time = fields.Datetime('入库时间', compute='_compute_location_status')
production_id = fields.Many2one('mrp.production', string='制造订单')
+ active = fields.Boolean("有效", default=True)
@api.depends('location_status')
def _compute_location_status(self):
@@ -326,6 +361,8 @@ class ShelfLocation(models.Model):
name = fields.Char('货位名称', required=True, size=20)
barcode = fields.Char('货位编码', copy=False, size=50)
+ qr_code = fields.Binary(string='二维码', compute='_compute_location_qr_code', store=True)
+
# 货架
shelf_id = fields.Many2one('sf.shelf', string='货架')
@@ -337,6 +374,62 @@ class ShelfLocation(models.Model):
def action_check(self):
self.check_state = 'enable'
+ @api.depends('barcode')
+ def _compute_location_qr_code(self):
+ for record in self:
+ if record.barcode:
+ # 创建一个QRCode对象
+ qr = qrcode.QRCode(
+ version=1, # 设置版本, 1-40,控制二维码的大小
+ error_correction=qrcode.constants.ERROR_CORRECT_L, # 设置错误校正等级
+ box_size=10, # 设置每个格子的像素大小
+ border=4, # 设置边框的格子宽度
+ )
+ # 添加数据
+ qr.add_data(record.barcode)
+ qr.make(fit=True)
+ # 创建二维码图像
+ img = qr.make_image(fill_color="black", back_color="white")
+ # 创建一个内存文件
+ buffer = io.BytesIO()
+ img.save(buffer, format="PNG") # 将图像保存到内存文件中
+ # 获取二进制数据
+ binary_data = buffer.getvalue()
+ # 使用Base64编码这些二进制数据
+ data = base64.b64encode(binary_data)
+ self.qr_code = data
+ else:
+ record.qr_code = False
+
+ def print_single_location_qr_code(self):
+ self.ensure_one()
+ qr_code_data = self.qr_code
+ if not qr_code_data:
+ raise UserError("没有找到二维码数据。")
+ barcode = self.barcode
+ # host = "192.168.50.110" # 可以根据实际情况修改
+ # port = 9100 # 可以根据实际情况修改
+ # 获取默认打印机配置
+ printer_config = self.env['printer.configuration'].sudo().search([('model', '=', self._name)], limit=1)
+ if not printer_config:
+ raise UserError('请先配置打印机')
+ host = printer_config.printer_id.ip_address
+ port = printer_config.printer_id.port
+ self.print_qr_code(barcode, host, port)
+ # 获取当前wizard的视图ID或其他标识信息
+ view_id = self.env.context.get('view_id')
+ # 构造返回wizard页面的action字典
+ action = {
+ 'type': 'ir.actions.act_window',
+ 'name': '返回 Wizard',
+ 'res_model': 'sf.shelf', # 替换为你的wizard模型名称
+ 'view_mode': 'form',
+ 'view_id': view_id, # 如果需要基于特定的视图返回
+ 'target': 'new', # 如果需要在新的窗口或标签页打开
+ 'res_id': self.shelf_id, # 如果你想要返回当前记录的视图
+ }
+ return action
+
# # 仓库类别(selection:库区、库位、货位)
# location_type = fields.Selection([
# ('货架', '货架'),
@@ -423,6 +516,19 @@ class ShelfLocation(models.Model):
else:
raise UserError("该库位无产品")
+ @api.model_create_multi
+ def create(self, vals_list):
+ # 编码重复校验
+ barcode_list = []
+ for val in vals_list:
+ location = self.search([('barcode', '=', val['barcode'])])
+ if location:
+ barcode_list.append(val['name'])
+ if barcode_list:
+ raise UserError("货位编码【%s】存在重复" % barcode_list)
+ records = super(ShelfLocation, self).create(vals_list)
+ return records
+
class Sf_stock_move_line(models.Model):
_name = 'stock.move.line'
@@ -438,7 +544,13 @@ class Sf_stock_move_line(models.Model):
current_product_id = fields.Integer(compute='_compute_location_dest_id_value', store=True)
there_is_no_sn = fields.Boolean('是否有序列号', default=False)
- rfid = fields.Char('Rfid', readonly=True)
+ rfid = fields.Char('Rfid')
+ rfid_barcode = fields.Char('Rfid', compute='_compute_rfid')
+
+ @api.depends('lot_id')
+ def _compute_rfid(self):
+ for item in self:
+ item.rfid_barcode = item.lot_id.rfid
def action_revert_inventory(self):
# 检查用户是否有执行操作的权限
@@ -446,7 +558,7 @@ class Sf_stock_move_line(models.Model):
raise UserError(_('抱歉,只有库管人员可以执行此动作'))
# 如果用户有权限,调用父类方法
- return super(CustomStockMoveLine, self).action_revert_inventory()
+ return super().action_revert_inventory()
@api.depends('lot_name')
def _compute_lot_qr_code(self):
@@ -713,35 +825,39 @@ class Sf_stock_move_line(models.Model):
destination_location_id = fields.Many2one(
'sf.shelf.location', string='目标货位')
- @api.onchange('destination_location_id')
- def _compute_destination_location_id(self):
+ def compute_destination_location_id(self):
for record in self:
+ obj = self.env['sf.shelf.location'].search([('name', '=',
+ self.destination_location_id.name)])
if record.lot_id:
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
else:
- obj = self.env['sf.shelf.location'].search([('name', '=',
- self.destination_location_id.name)])
if obj:
obj.product_id = record.product_id.id
# obj.location_status = '占用'
obj.product_num += record.reserved_uom_qty
+ @api.onchange('destination_location_id')
+ def _check_destination_location_id(self):
+ for item in self:
+ if item:
+ i = 0
+ barcode = item.destination_location_id.barcode
+ for line in item.picking_id.move_line_ids_without_package:
+ if barcode and barcode == line.destination_location_id.barcode:
+ i += 1
+ if i > 1:
+ raise ValidationError(
+ '【%s】货位已经被占用,请重新选择!!!' % item.destination_location_id.barcode)
+
class SfStockPicking(models.Model):
_inherit = 'stock.picking'
@@ -764,12 +880,15 @@ class SfStockPicking(models.Model):
res = super(SfStockPicking, self).button_validate()
for line in self.move_line_ids:
if line:
+ # 调用入库方法进行入库
+ line.compute_destination_location_id()
if line.current_location_id:
if line.current_location_id.product_sn_id:
line.current_location_id.product_sn_id = False
# line.current_location_id.location_status = '空闲'
line.current_location_id.product_num = 0
+ # 对入库作业的刀柄和托盘进行Rfid绑定校验
for move in self.move_ids:
if move and move.product_id.cutting_tool_material_id.name == '刀柄' or '托盘' in (
move.product_id.fixture_material_id.name or ''):
@@ -859,15 +978,27 @@ class SfProcurementGroup(models.Model):
return res
+# class SfPickingType(models.Model):
+# _inherit = 'stock.picking.type'
+#
+# def _default_show_operations(self):
+# return self.user_has_groups('stock.group_production_lot,'
+# 'stock.group_stock_multi_locations,'
+# 'stock.group_tracking_lot',
+# 'sf_warehouse.group_sf_stock_user',
+# 'sf_warehouse.group_sf_stock_manager')
+
class SfPickingType(models.Model):
_inherit = 'stock.picking.type'
def _default_show_operations(self):
- return self.user_has_groups('stock.group_production_lot,'
- 'stock.group_stock_multi_locations,'
- 'stock.group_tracking_lot',
- 'sf_warehouse.group_sf_stock_user',
- 'sf_warehouse.group_sf_stock_manager')
+ return self.user_has_groups(
+ 'stock.group_production_lot,'
+ 'stock.group_stock_multi_locations,'
+ 'stock.group_tracking_lot,'
+ 'sf_warehouse.group_sf_stock_user,'
+ 'sf_warehouse.group_sf_stock_manager'
+ )
class CustomStockMove(models.Model):
diff --git a/sf_warehouse/security/ir.model.access.csv b/sf_warehouse/security/ir.model.access.csv
index db246adc..a5b3b2fa 100644
--- a/sf_warehouse/security/ir.model.access.csv
+++ b/sf_warehouse/security/ir.model.access.csv
@@ -132,6 +132,9 @@ access_sf_cutting_tool_material_group_sf_stock_manager,sf_cutting_tool_material_
access_sf_cutting_tool_standard_library_group_sf_stock_manager,sf_cutting_tool_standard_library_group_sf_stock_manager,sf_base.model_sf_cutting_tool_standard_library,sf_warehouse.group_sf_stock_manager,1,0,1,0
access_sf_tool_materials_basic_parameters_group_sf_stock_manager,sf_tool_materials_basic_parameters_group_sf_stock_manager,sf_base.model_sf_tool_materials_basic_parameters,sf_warehouse.group_sf_stock_manager,1,0,1,0
+access_sf_shelf_location_wizard_group_plan_dispatch,sf_shelf_location_wizard_group_plan_dispatch,model_sf_shelf_location_wizard,sf_base.group_plan_dispatch,1,0,0,0
+access_sf_shelf_location_wizard_group_sf_stock_user_group_sf_stock_user,sf_shelf_location_wizard_group_sf_stock_user_group_sf_stock_user,model_sf_shelf_location_wizard,sf_warehouse.group_sf_stock_user,1,0,0,0
+access_sf_shelf_location_wizard_group_sf_stock_manager,sf_shelf_location_wizard_group_sf_stock_manager,model_sf_shelf_location_wizard,sf_warehouse.group_sf_stock_manager,1,1,1,0
diff --git a/sf_warehouse/views/change_stock_move_views.xml b/sf_warehouse/views/change_stock_move_views.xml
index 0f328b4e..519eca85 100644
--- a/sf_warehouse/views/change_stock_move_views.xml
+++ b/sf_warehouse/views/change_stock_move_views.xml
@@ -19,8 +19,8 @@
-
+ '=', there_is_no_sn)]" options="{'no_create': True,'no_create_edit':True}"/>
+
diff --git a/sf_warehouse/views/shelf_location.xml b/sf_warehouse/views/shelf_location.xml
index 9cd4446a..a0e1a5c1 100644
--- a/sf_warehouse/views/shelf_location.xml
+++ b/sf_warehouse/views/shelf_location.xml
@@ -1,7 +1,7 @@
-
+
Sf Shelf
sf.shelf
@@ -9,7 +9,8 @@
@@ -46,23 +56,23 @@
-
+
货架
ir.actions.act_window
sf.shelf
tree,form
-
+
-
+
+ id="sf_shelf_menu"
+ name="货架"
+ parent="stock.menu_warehouse_config"
+ sequence="19"
+ action="sf_shelf_action"
+ groups="sf_warehouse.group_sf_stock_user"/>
@@ -116,13 +126,25 @@
Shelf Location form
sf.shelf.location
-