|
-
- from .utils import create_name, safe_division
-
- from odoo.exceptions import UserError
- from odoo import models
- from odoo import fields
- from odoo import api
- from odoo.tools import float_compare, float_is_zero
-
-
- class WhInventory(models.Model):
- _name = 'wh.inventory'
- _description = '盘点单'
- _inherit = ['mail.thread']
- _order = 'date DESC, id DESC'
-
- INVENTORY_STATE = [
- ('draft', '草稿'),
- ('query', '查询中'),
- ('confirmed', '待确认盘盈盘亏'),
- ('done', '完成'),
- ]
-
- @api.model
- def _get_default_warehouse_impl(self):
- if self.env.context.get('warehouse_type', 'stock'):
- return self.env['warehouse'].get_warehouse_by_type(
- self.env.context.get('warehouse_type', 'stock'))
-
- @api.model
- def _get_default_warehouse(self):
- '''获取盘点仓库'''
- return self._get_default_warehouse_impl()
-
- date = fields.Date('日期', default=fields.Date.context_today,
- help='盘点单创建日期,默认为当前天')
- name = fields.Char('名称', copy=False, default='/',
- help='单据编号,创建时会自动生成')
- warehouse_id = fields.Many2one('warehouse', '仓库', required=True, default=_get_default_warehouse,
- help='盘点单盘点的仓库')
- goods = fields.Many2many('goods', string='商品',
- help='盘点单盘点的商品')
- out_id = fields.Many2one('wh.out', '盘亏单据', copy=False,
- help='盘亏生成的其他出库单单据')
- in_id = fields.Many2one('wh.in', '盘盈单据', copy=False,
- help='盘盈生成的其他入库单单据')
- state = fields.Selection(
- INVENTORY_STATE, '状态', copy=False, default='draft',
- index=True,
- tracking=True,
- help='盘点单状态,新建时状态为草稿;'
- '点击查询后为确认后状态为查询中;'
- '有盘亏盘盈时生成的其他出入库单没有确认时状态为待确认盘盈盘亏;'
- '盘亏盘盈生成的其他出入库单确认后状态为完成')
- line_ids = fields.One2many(
- 'wh.inventory.line', 'inventory_id', '明细', copy=False,
- help='盘点单的明细行')
- note = fields.Text('备注',
- help='可以为该单据添加一些需要的标识信息')
- company_id = fields.Many2one(
- 'res.company',
- string='公司',
- change_default=True,
- default=lambda self: self.env.company)
-
-
- def requery_inventory(self):
- self.delete_confirmed_wh()
- self.state = 'query'
-
- @api.model
- @create_name
- def create(self, vals):
- return super(WhInventory, self).create(vals)
-
-
- def unlink(self):
- for inventory in self:
- inventory.delete_confirmed_wh()
-
- return super(WhInventory, self).unlink()
-
- def delete_confirmed_wh(self):
- for inventory in self:
- if inventory.state == 'confirmed':
- if (inventory.out_id and inventory.out_id.state == 'done') \
- or (inventory.in_id and inventory.in_id.state == 'done'):
- raise UserError('请先撤销掉相关的盘盈盘亏单据')
- else:
- inventory.out_id.unlink()
- inventory.in_id.unlink()
-
- return True
-
- def check_done(self):
- for inventory in self:
- if inventory.state == 'confirmed' and \
- (not inventory.out_id or inventory.out_id.state == 'done') and \
- (not inventory.in_id or inventory.in_id.state == 'done'):
- self.state = 'done'
- return True
- if inventory.state == 'done' and \
- (not inventory.out_id or inventory.out_id.state != 'done') and \
- (not inventory.in_id or inventory.in_id.state != 'done'):
- self.state = 'confirmed'
- return True
- return False
-
-
- def open_out(self):
- for inventory in self:
- return {
- 'type': 'ir.actions.act_window',
- 'res_model': 'wh.out',
- 'view_mode': 'form',
- 'res_id': inventory.out_id.id,
- }
-
-
- def open_in(self):
- for inventory in self:
- return {
- 'type': 'ir.actions.act_window',
- 'res_model': 'wh.in',
- 'view_mode': 'form',
- 'res_id': inventory.in_id.id,
- }
-
- def delete_line(self):
- self.line_ids.unlink()
-
- def create_losses_out(self, inventory, out_line):
- inventory_warehouse = self.env.ref('warehouse.warehouse_inventory')
- out_vals = {
- 'warehouse_dest_id': inventory_warehouse.id,
- 'warehouse_id': inventory.warehouse_id.id,
- 'type': 'inventory',
- 'line_out_ids': [],
- }
-
- for line in out_line:
- out_vals['line_out_ids'].append(
- [0, False, line.get_move_line(wh_type='out')])
-
- out_id = self.env['wh.out'].create(out_vals)
- inventory.out_id = out_id
-
- def create_overage_in(self, inventory, in_line):
- inventory_warehouse = self.env.ref('warehouse.warehouse_inventory')
- in_vals = {
- 'warehouse_dest_id': inventory.warehouse_id.id,
- 'warehouse_id': inventory_warehouse.id,
- 'type': 'inventory',
- 'line_in_ids': [],
- }
-
- for line in in_line:
- in_vals['line_in_ids'].append(
- [0, False, line.get_move_line(wh_type='in')])
-
- in_id = self.env['wh.in'].create(in_vals)
- inventory.in_id = in_id
-
-
- def generate_inventory(self):
- for inventory in self:
- if self.state in ['done', 'confirmed']:
- raise UserError('请不要重复点击生成盘点单据按钮')
- out_line, in_line = [], []
- for line in inventory.line_ids:
- if line.difference_qty < 0:
- out_line.append(line)
- elif line.difference_qty > 0:
- in_line.append(line)
-
- if out_line:
- self.create_losses_out(inventory, out_line)
-
- if in_line:
- self.create_overage_in(inventory, in_line)
-
- if len(out_line) + len(in_line) == 0:
- inventory.state = 'done'
-
- if out_line or in_line:
- inventory.state = 'confirmed'
-
- return True
-
- def get_line_detail(self):
- for inventory in self:
- sql_text = '''
- SELECT wh.id as warehouse_id,
- goods.id as goods_id,
- line.attribute_id as attribute_id,
- line.lot as lot,
- uom.id as uom_id,
- uos.id as uos_id,
- sum(line.qty_remaining) as qty,
- sum(line.uos_qty_remaining) as uos_qty
-
- FROM wh_move_line line
- LEFT JOIN goods goods ON line.goods_id = goods.id
- LEFT JOIN uom uom ON goods.uom_id = uom.id
- LEFT JOIN uom uos ON goods.uos_id = uos.id
- LEFT JOIN warehouse wh ON line.warehouse_dest_id = wh.id
-
- WHERE line.qty_remaining != 0
- AND wh.type = 'stock'
- AND line.state = 'done'
- %s
-
- GROUP BY
- wh.id, line.lot, line.attribute_id, goods.id, uom.id, uos.id
-
- ORDER BY
- goods.id, line.lot
- '''
-
- extra_text = ' AND wh.id = %s' % inventory.warehouse_id.id
-
- if inventory.goods:
- goods_ids = inventory.goods.ids
- goods_ids.append(0)
- extra_text += " AND goods.id IN {ids}".format(
- ids=tuple(set(goods_ids)))
-
- inventory.env.cr.execute(sql_text % extra_text)
- res = inventory.env.cr.dictfetchall()
- for line in res:
- # 盘点单查询的盘点数量不应该包含移库在途的 #1358
- for int_line in self.env['wh.move.line'].search(
- [('goods_id', '=', line['goods_id']),
- ('attribute_id', '=', line['attribute_id']),
- ('lot_id', '=', line['lot']),
- ('warehouse_id', '=', line['warehouse_id']),
- ('type', '=', 'internal'),
- ('state', '=', 'draft')]):
- line['qty'] -= int_line.goods_qty
- line['uos_qty'] -= int_line.goods_uos_qty
- if not line['qty']:
- res.remove(line)
- return res
-
-
- def query_inventory(self):
- line_obj = self.env['wh.inventory.line']
- for inventory in self:
- inventory.delete_line()
- line_ids = inventory.get_line_detail()
- for line in line_ids:
- line_obj.create_wh_inventory_line_by_data(inventory.id, line)
- if line_ids:
- inventory.state = 'query'
- return True
-
-
- class WhInventoryLine(models.Model):
- _name = 'wh.inventory.line'
- _description = '盘点单明细'
-
- LOT_TYPE = [
- ('out', '出库'),
- ('in', '入库'),
- ('nothing', '不做处理'),
- ]
-
-
- @api.depends('inventory_qty', 'real_qty', 'inventory_uos_qty', 'real_uos_qty')
- def _get_difference_qty(self):
- for line in self:
- line.difference_qty = line.inventory_qty - line.real_qty
- line.difference_uos_qty = line.inventory_uos_qty - line.real_uos_qty
-
- if float_is_zero(line.difference_qty, 2) and not float_is_zero(line.difference_uos_qty, 2):
- line.difference_qty = line.difference_uos_qty * line.goods_id.conversion
- if not float_is_zero(line.difference_qty, 2) and line.difference_uos_qty == 0:
- line.difference_uos_qty = line.difference_qty / line.goods_id.conversion
-
-
- @api.depends('inventory_uos_qty', 'real_uos_qty')
- def _get_difference_uos_qty(self):
- for line in self:
- line.difference_uos_qty = line.inventory_uos_qty - line.real_uos_qty
-
- inventory_id = fields.Many2one('wh.inventory', '盘点', ondelete='cascade',
- help='盘点单行对应的盘点单')
- warehouse_id = fields.Many2one('warehouse', '仓库', required=True, ondelete='restrict',
- help='盘点单行对应的仓库')
- goods_id = fields.Many2one('goods', '商品', required=True, ondelete='restrict',
- help='盘点单行对应的商品')
- attribute_id = fields.Many2one('attribute', '属性', ondelete='restrict',
- help='盘点单行对应的商品的属性')
- using_batch = fields.Boolean(
- related='goods_id.using_batch', string='批号管理',
- help='盘点单行对应的商品是否使用批号管理,是True否则False')
- force_batch_one = fields.Boolean(
- related='goods_id.force_batch_one', string='每批号数量为1',
- help='盘点单行对应的商品是否使用每批号数量为1,是True否则False')
- lot = fields.Char('批号',
- help='盘点单行对应的商品批号')
- new_lot = fields.Char('盘盈批号',
- help='盘点单行对应的商品盘盈批号')
- new_lot_id = fields.Many2one('wh.move.line', '盘亏批号',
- ondelete='restrict',
- help='盘点单行对应的商品盘亏批号')
- lot_type = fields.Selection(LOT_TYPE, '批号类型', default='nothing',
- help='批号类型: 出库、入库、不做处理')
- uom_id = fields.Many2one('uom', '单位', ondelete='restrict',
- help='盘点单行对应的商品的计量单位')
- uos_id = fields.Many2one('uom', '辅助单位', ondelete='restrict',
- help='盘点单行对应的商品的辅助单位')
- real_qty = fields.Float(
- '账面数量', digits='Quantity',
- help='盘点单行对应的商品的账面数量')
- real_uos_qty = fields.Float(
- '账面辅助数量', digits='Quantity',
- help='盘点单行对应的商品的账面辅助数量')
- inventory_qty = fields.Float(
- '实际数量', digits='Quantity',
- required=True,
- help='盘点单行对应的商品的实际数量')
- inventory_uos_qty = fields.Float(
- '实际辅助数量', digits='Quantity',
- required=True,
- help='盘点单行对应的商品的实际辅助数量')
- difference_qty = fields.Float(
- '差异数量', digits='Quantity',
- compute='_get_difference_qty',
- help='盘点单行对应的商品的差异数量')
- difference_uos_qty = fields.Float(
- '差异辅助数量', digits='Quantity',
- compute='_get_difference_uos_qty',
- help='盘点单行对应的商品的差异辅助数量')
- company_id = fields.Many2one(
- 'res.company',
- string='公司',
- change_default=True,
- default=lambda self: self.env.company)
-
- def check_difference_identical(self):
- if self.difference_qty * self.difference_uos_qty < 0:
- self.inventory_qty = self.real_qty
- self.inventory_uos_qty = self.real_uos_qty
-
- return {'warning': {
- 'title': '错误',
- 'message': '盘盈盘亏数量应该与辅助单位的盘盈盘亏数量盈亏方向一致',
- }}
-
- def create_wh_inventory_line_by_data(self, inventory_id, line_data):
- self.create({
- 'inventory_id': inventory_id,
- 'warehouse_id': line_data.get('warehouse_id'),
- 'goods_id': line_data.get('goods_id'),
- 'attribute_id': line_data.get('attribute_id'),
- 'lot': line_data.get('lot'),
- 'uom_id': line_data.get('uom_id'),
- 'uos_id': line_data.get('uos_id'),
- 'real_qty': line_data.get('qty'),
- 'real_uos_qty': line_data.get('uos_qty'),
- 'inventory_qty': line_data.get('qty'),
- 'inventory_uos_qty': line_data.get('uos_qty'),
- })
-
- def line_role_back(self):
- self.inventory_qty = self.real_qty
- self.inventory_uos_qty = self.real_uos_qty
- self.difference_qty = 0
- self.difference_uos_qty = 0
- self.new_lot = ''
- self.new_lot_id = False
- self.lot_type = 'nothing'
-
-
- @api.onchange('inventory_qty')
- def onchange_qty(self):
- self.ensure_one()
-
- if self.goods_id and self.goods_id.using_batch:
- if self.goods_id.force_batch_one and self.difference_qty:
- flag = self.difference_qty > 0 and 1 or -1
- if abs(self.difference_qty) != 1:
- self.line_role_back()
- return {'warning': {
- 'title': '警告',
- 'message': '商品上设置了序号为1,此时一次只能盘亏或盘盈一个商品数量',
- }}
-
- if self.difference_qty > 0:
- self.lot_type = 'in'
- self.new_lot = self.new_lot or self.lot
- self.new_lot_id = False
- elif self.difference_qty < 0:
- self.lot_type = 'out'
- self.new_lot = ''
- else:
- self.lot_type = 'nothing'
- self.new_lot_id = False
- self.new_lot = ''
-
- return self.check_difference_identical()
-
- @api.onchange('inventory_uos_qty')
- def onchange_uos_qty(self):
- self.inventory_qty = self.goods_id.conversion_unit(
- self.inventory_uos_qty)
-
- def get_move_line(self, wh_type='in', context=None):
- inventory_warehouse = self.env['warehouse'] \
- .get_warehouse_by_type('inventory')
- for inventory in self:
- cost, cost_unit = inventory.goods_id. \
- get_suggested_cost_by_warehouse(
- inventory.warehouse_id, abs(inventory.difference_qty),
- lot_id=inventory.new_lot_id,
- attribute=inventory.attribute_id)
-
- if wh_type == 'out':
- lot_id = self.env['wh.move.line'].search([('warehouse_dest_id', '=', inventory.warehouse_id.id),
- ('goods_id', '=', inventory.goods_id.id),
- ('lot', '=', inventory.lot),
- ('state', '=', 'done')
- ], limit=1)
- res = {
- 'type': wh_type,
- 'lot': inventory.lot,
- 'lot_id': lot_id.id,
- 'goods_id': inventory.goods_id.id,
- 'attribute_id': inventory.attribute_id.id,
- 'uom_id': inventory.uom_id.id,
- 'uos_id': inventory.uos_id.id,
- 'cost_unit': cost_unit,
- 'cost': cost,
- }
-
- elif wh_type == 'in':
- res = {
- 'type': wh_type,
- 'lot': inventory.new_lot,
- 'lot_id': inventory.new_lot_id.id,
- 'goods_id': inventory.goods_id.id,
- 'attribute_id': inventory.attribute_id.id,
- 'uom_id': inventory.uom_id.id,
- 'uos_id': inventory.uos_id.id,
- 'cost_unit': cost_unit,
- 'cost': cost,
- }
-
- difference_qty, difference_uos_qty = abs(
- inventory.difference_qty), abs(inventory.difference_uos_qty)
-
- res.update({'goods_qty': difference_qty})
-
- return res
-
-
- class WhOut(models.Model):
- _inherit = 'wh.out'
-
- inventory_ids = fields.One2many('wh.inventory', 'out_id', '盘点单',
- help='其他出库单行对应的盘点单行,盘亏的情况')
-
-
- def approve_order(self):
- res = super(WhOut, self).approve_order()
- for order in self:
- order.inventory_ids.check_done()
-
- return res
-
-
- def cancel_approved_order(self):
- res = super(WhOut, self).cancel_approved_order()
- for order in self:
- order.inventory_ids.check_done()
-
- return res
-
-
- class WhIn(models.Model):
- _inherit = 'wh.in'
-
- inventory_ids = fields.One2many('wh.inventory', 'in_id', '盘点单',
- help='其他入库单行对应的盘点单行,盘盈的情况')
-
-
- def approve_order(self):
- res = super(WhIn, self).approve_order()
- for order in self:
- order.inventory_ids.check_done()
-
- return res
-
-
- def cancel_approved_order(self):
- res = super(WhIn, self).cancel_approved_order()
- for order in self:
- order.inventory_ids.check_done()
-
- return res
|