| 
							- from odoo import models, fields, api
 - from odoo.exceptions import UserError
 - 
 - 
 - class WhMove(models.Model):
 -     _name = 'wh.move'
 -     _description = '移库单'
 - 
 -     MOVE_STATE = [
 -         ('draft', '草稿'),
 -         ('done', '已完成'),
 -         ('cancel', '已作废'),]
 - 
 -     @api.depends('line_out_ids', 'line_in_ids')
 -     def _compute_total_qty(self):
 -         for wm in self:
 -             goods_total = 0
 -             if wm.line_in_ids:
 -                 # 入库商品总数
 -                 goods_total = sum(line.goods_qty for line in wm.line_in_ids)
 -             elif wm.line_out_ids:
 -                 # 出库商品总数
 -                 goods_total = sum(line.goods_qty for line in wm.line_out_ids)
 -             wm.total_qty = goods_total
 - 
 -     @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_dest_impl(self):
 -         if self.env.context.get('warehouse_dest_type', 'stock'):
 -             return self.env['warehouse'].get_warehouse_by_type(
 -                 self.env.context.get('warehouse_dest_type', 'stock'))
 - 
 -     @api.model
 -     def _get_default_warehouse(self):
 -         '''获取调出仓库'''
 -         return self._get_default_warehouse_impl()
 - 
 -     @api.model
 -     def _get_default_warehouse_dest(self):
 -         '''获取调入仓库'''
 -         return self._get_default_warehouse_dest_impl()
 - 
 -     origin = fields.Char('移库类型', required=True,
 -                          help='移库类型')
 -     name = fields.Char('单据编号', copy=False, default='/',
 -                        help='单据编号,创建时会自动生成')
 -     ref = fields.Char('外部单号')
 -     state = fields.Selection(MOVE_STATE, '状态', copy=False, default='draft',
 -                              index=True,
 -                              help='移库单状态标识,新建时状态为草稿;确认后状态为已确认')
 -     partner_id = fields.Many2one('partner', '业务伙伴', ondelete='restrict',
 -                                  help='该单据对应的业务伙伴')
 -     date = fields.Date('单据日期', required=True, copy=False, default=fields.Date.context_today,
 -                        help='单据创建日期,默认为当前天')
 -     warehouse_id = fields.Many2one('warehouse', '调出仓库',
 -                                    ondelete='restrict',
 -                                    required=True,
 -                                    domain="['|',('user_ids','=',False),('user_ids','in',uid)]",
 -                                    default=_get_default_warehouse,
 -                                    help='移库单的来源仓库')
 -     warehouse_dest_id = fields.Many2one('warehouse', '调入仓库',
 -                                         ondelete='restrict',
 -                                         required=True,
 -                                         domain="['|',('user_ids','=',False),('user_ids','in',uid)]",
 -                                         default=_get_default_warehouse_dest,
 -                                         help='移库单的目的仓库')
 -     approve_uid = fields.Many2one('res.users', '确认人',
 -                                   copy=False, ondelete='restrict',
 -                                   help='移库单的确认人')
 -     approve_date = fields.Datetime('确认日期', copy=False)
 -     line_ids = fields.One2many('wh.move.line', 'move_id', '出入库明细', copy=False)
 -     line_out_ids = fields.One2many('wh.move.line', 'move_id', '出库明细',
 -                                    domain=[
 -                                        ('type', 'in', ['out', 'internal'])],
 -                                    copy=True,
 -                                    help='出库类型的移库单对应的出库明细')
 -     line_in_ids = fields.One2many('wh.move.line', 'move_id', '入库明细',
 -                                   domain=[('type', '=', 'in')],
 -                                   context={'type': 'in'}, copy=True,
 -                                   help='入库类型的移库单对应的入库明细')
 -     in_goods_id = fields.Many2one('goods', string='入库商品',
 -                                   related='line_in_ids.goods_id')
 -     out_goods_id = fields.Many2one('goods', string='出库商品',
 -                                    related='line_out_ids.goods_id')
 -     auxiliary_id = fields.Many2one(
 -         'auxiliary.financing', '辅助核算', help='辅助核算是对账务处理的一种补充,即实现更广泛的账务处理,\
 -         以适应企业管理和决策的需要.辅助核算一般通过核算项目来实现', ondelete='restrict')
 -     note = fields.Text('备注',
 -                        copy=False,
 -                        help='可以为该单据添加一些需要的标识信息')
 -     total_qty = fields.Float('商品总数', compute=_compute_total_qty,
 -                              digits='Quantity', store=True,
 -                              help='该移库单的入/出库明细行包含的商品总数')
 -     user_id = fields.Many2one(
 -         'res.users',
 -         '经办人',
 -         ondelete='restrict',
 -         default=lambda self: self.env.user,
 -         help='单据经办人'
 -     )
 -     express_type = fields.Char(string='承运商')
 -     express_code = fields.Char('快递单号', copy=False)
 -     company_id = fields.Many2one(
 -         'res.company',
 -         string='公司',
 -         change_default=True,
 -         default=lambda self: self.env.company)
 -     qc_result = fields.Binary('质检报告',
 -                               help='点击上传质检报告')
 -     qc_result_summary = fields.Text('质检结果概述',
 -                               help='质检结果概述')
 -     finance_category_id = fields.Many2one(
 -         'core.category',
 -         string='收发类别',
 -         ondelete='restrict',
 -         domain=[('type', '=', 'finance'), ('note', '!=', '由系统创建')],
 -         context={'type': 'finance'},
 -         help='生成凭证时从此字段上取商品科目的对方科目',
 -     )
 - 
 -     details = fields.Html('明细',compute='_compute_details')
 - 
 -     @api.depends('line_in_ids', 'line_out_ids')
 -     def _compute_details(self):
 -         for v in self:
 -             vl = {'col':[],'val':[]}
 -             vl['col'] = ['商品','数量']
 -             for l in v.line_in_ids:
 -                 vl['val'].append([l.goods_id.name,l.goods_qty])
 -             for l in v.line_out_ids:
 -                 vl['val'].append([l.goods_id.name,l.goods_qty])
 -             v.details = v.company_id._get_html_table(vl)
 - 
 -     def scan_barcode_move_line_operation(self, line, conversion):
 -         """
 -         在原移库明细行中更新数量和辅助数量,不创建新行
 -         :return:
 -         """
 -         line.goods_qty += 1
 -         line.goods_uos_qty = line.goods_qty / conversion
 -         return True
 - 
 -     def scan_barcode_inventory_line_operation(self, line, conversion):
 -         '''盘点单明细行数量增加'''
 -         line.inventory_qty += 1
 -         line.inventory_uos_qty = line.inventory_qty / conversion
 -         line.difference_qty += 1
 -         line.difference_uos_qty = line.difference_qty / conversion
 - 
 -         return True
 - 
 -     def scan_barcode_move_in_out_operation(self, move, att, conversion, goods, val):
 -         """
 -         对仓库各种移库单据上扫码的统一处理
 -         :return: 是否创建新的明细行
 -         """
 -         create_line = False
 -         loop_field = 'line_in_ids' if val['type'] == 'in' else 'line_out_ids'
 -         for line in move[loop_field]:
 -             line.cost_unit = (line.goods_id.price if val['type'] in ['out', 'internal']
 -                               else line.goods_id.cost)  # 其他出入库单 、内部调拨单
 -             line.price_taxed = (line.goods_id.price if val['type'] == 'out'
 -                                 else line.goods_id.cost)  # 采购或销售单据
 -             # 如果商品属性或商品上存在条码,且明细行上已经存在该商品,则数量累加
 -             if (att and line.attribute_id == att) or (goods and line.goods_id == goods):
 -                 create_line = self.scan_barcode_move_line_operation(
 -                     line, conversion)
 -         return create_line
 - 
 -     def scan_barcode_inventory_operation(self, move, att, conversion, goods, val):
 -         '''盘点单扫码操作'''
 -         create_line = False
 -         for line in move.line_ids:
 -             # 如果商品属性上存在条码 或 商品上存在条码
 -             if (att and line.attribute_id == att) or (goods and line.goods_id == goods):
 -                 create_line = self.scan_barcode_inventory_line_operation(
 -                     line, conversion)
 -         return create_line
 - 
 -     def scan_barcode_each_model_operation(self, model_name, order_id, att, goods, conversion):
 -         val = {}
 -         create_line = False  # 是否创建新的明细行
 -         order = self.env[model_name].browse(order_id)
 -         if model_name in ['wh.out', 'wh.in', 'wh.internal']:
 -             move = order.move_id
 -         # 在其他出库单上扫描条码
 -         if model_name == 'wh.out':
 -             val['type'] = 'out'
 -         # 在其他入库单上扫描条码
 -         if model_name == 'wh.in':
 -             val['type'] = 'in'
 -         # 销售出入库单的二维码
 -         if model_name == 'sell.delivery':
 -             move = order.sell_move_id
 -             val['type'] = order.is_return and 'in' or 'out'
 -         # 采购出入库单的二维码
 -         if model_name == 'buy.receipt':
 -             move = order.buy_move_id
 -             val['type'] = order.is_return and 'out' or 'in'
 -         # 调拔单的扫描条码
 -         if model_name == 'wh.internal':
 -             val['type'] = 'internal'
 -         if model_name != 'wh.inventory':
 -             create_line = self.scan_barcode_move_in_out_operation(
 -                 move, att, conversion, goods, val)
 - 
 -         # 盘点单的扫码
 -         if model_name == 'wh.inventory':
 -             move = order
 -             create_line = self.scan_barcode_inventory_operation(
 -                 move, att, conversion, goods, val)
 - 
 -         return move, create_line, val
 - 
 -     
 -     def check_goods_qty(self, goods, attribute, warehouse):
 -         '''SQL来取指定商品,属性,仓库,的当前剩余数量'''
 - 
 -         if attribute:
 -             change_conditions = "AND line.attribute_id = %s" % attribute.id
 -         elif goods:
 -             change_conditions = "AND line.goods_id = %s" % goods.id
 -         else:
 -             change_conditions = "AND 1 = 0"
 -         self.env.cr.execute('''
 -                        SELECT sum(line.qty_remaining) as qty
 -                        FROM wh_move_line line
 - 
 -                        WHERE line.warehouse_dest_id = %s
 -                              AND line.state = 'done'
 -                              %s
 -                    ''' % (warehouse.id, change_conditions,))
 -         return self.env.cr.fetchone()
 - 
 -     def prepare_move_line_data(self, att, val, goods, move):
 -         """
 -         准备移库单明细数据
 -         :return: 字典
 -         """
 -         # 若传入的商品属性 att 上条码存在则取属性对应的商品,否则取传入的商品 goods
 -         goods = att and att.goods_id or goods
 -         goods_id = goods.id
 -         uos_id = goods.uos_id.id
 -         uom_id = goods.uom_id.id
 -         tax_rate = goods.tax_rate
 -         attribute_id = att and att.id or False
 -         conversion = goods.conversion
 -         # 采购入库取成本价,销售退货取销售价;采购退货取成本价,销售发货取销售价
 -         price_taxed = move._name == 'buy.receipt' and goods.cost or goods.price
 -         cost_unit = val.get('type') != 'in' and 0 or goods.cost
 - 
 -         val.update({
 -             'goods_id': goods_id,
 -             'attribute_id': attribute_id,
 -             'warehouse_id': move.warehouse_id.id,
 -             'uos_id': uos_id,
 -             'uom_id': uom_id,
 -         })
 -         if move._name != 'wh.inventory':
 -             val.update({
 -                 'warehouse_dest_id': move.warehouse_dest_id.id,
 -                 'goods_uos_qty': 1.0 / conversion,
 -                 'goods_qty': 1,
 -                 'price_taxed': price_taxed,
 -                 'tax_rate': tax_rate,
 -                 'cost_unit': cost_unit,
 -                 'move_id': move.id})
 -         else:
 -             val.update({
 -                 'inventory_uos_qty': 1.0 / conversion,
 -                 'inventory_qty': 1,
 -                 'real_uos_qty': 0,
 -                 'real_qty': 0,
 -                 'difference_uos_qty': 1.0 / conversion,
 -                 'difference_qty': 1,
 -                 'inventory_id': move.id})
 -         return val
 - 
 -     @api.model
 -     def check_barcode(self, model_name, order_id, att, goods):
 -         pass
 - 
 -     @api.model
 -     def scan_barcode(self, model_name, barcode, order_id):
 -         """
 -         扫描条码
 -         :param model_name: 模型名
 -         :param barcode: 条码
 -         :param order_id: 单据id
 -         :return:
 -         """
 -         att = self.env['attribute'].search([('ean', '=', barcode)])
 -         goods = self.env['goods'].search([('barcode', '=', barcode)])
 -         line_model = (model_name == 'wh.inventory' and 'wh.inventory.line'
 -                       or 'wh.move.line')
 - 
 -         if not att and not goods:
 -             raise UserError('条码为  %s 的商品不存在' % (barcode))
 -         else:
 -             self.check_barcode(model_name, order_id, att, goods)
 -             conversion = att and att.goods_id.conversion or goods.conversion
 -             move, create_line, val = self.scan_barcode_each_model_operation(
 -                 model_name, order_id, att, goods, conversion)
 -             if not create_line:
 -                 self.env[line_model].create(
 -                     self.prepare_move_line_data(att, val, goods, move))
 - 
 -     def check_qc_result(self):
 -         """
 -         检验质检报告是否上传
 -         :return:
 -         """
 -         qc_rule = self.env['qc.rule'].search([
 -             ('move_type', '=', self.origin),
 -             ('warehouse_id', '=', self.warehouse_id.id),
 -             ('warehouse_dest_id', '=', self.warehouse_dest_id.id)])
 -         if qc_rule and not self.qc_result:
 -             raise UserError('请先上传质检报告')
 - 
 -     def prev_approve_order(self):
 -         """
 -         确认单据之前所做的处理
 -         :return:
 -         """
 -         for order in self:
 -             if not order.line_out_ids and not order.line_in_ids:
 -                 raise UserError('单据的明细行不可以为空')
 -             order.check_qc_result()
 - 
 -     
 -     def approve_order(self):
 -         """
 -         确认单据
 -         :return:
 -         """
 -         for order in self:
 -             order.prev_approve_order()
 -             order.line_out_ids.action_done()
 -             order.line_in_ids.action_done()
 - 
 -         # 每次移库完成,清空库位上商品数量为0的商品和属性(不合逻辑的数据)
 -         for loc in self.env['location'].search([('save_qty', '=', 0),
 -                                                 ('goods_id', '!=', False)
 -                                                 ]):
 -             if not loc.current_qty:
 -                 continue    # pragma: no cover
 -         return self.write({
 -             'approve_uid': self.env.uid,
 -             'approve_date': fields.Datetime.now(self),
 -         })
 - 
 -     def prev_cancel_approved_order(self):
 -         pass
 - 
 -     
 -     def cancel_approved_order(self):
 -         """
 -         撤销确认单据
 -         :return:
 -         """
 -         for order in self:
 -             order.prev_cancel_approved_order()
 -             order.line_out_ids.action_draft()
 -             order.line_in_ids.action_draft()
 - 
 -         return self.write({
 -             'approve_uid': False,
 -             'approve_date': False,
 -         })
 - 
 -     def write(self, vals):
 -         """
 -         作废明细行
 -         """
 -         if vals.get('state', False) == 'cancel':
 -             for order in self:
 -                 order.line_out_ids.action_cancel()
 -                 order.line_in_ids.action_cancel()
 - 
 -         return super(WhMove, self).write(vals)
 - 
 -     def create_zero_wh_in(self, wh_in, model_name):
 -         """
 -         创建一个缺货向导
 -         :param wh_in: 单据实例
 -         :param model_name: 单据模型
 -         :return:
 -         """
 -         all_line_message = ""
 -         today = fields.Datetime.now()
 -         line_in_ids = []
 -         goods_list = []
 -         for line in wh_in.line_out_ids:
 -             if line.goods_id.no_stock:
 -                 continue
 - 
 -             result = self.check_goods_qty(
 -                 line.goods_id, line.attribute_id, wh_in.warehouse_id)
 -             result = result[0] or 0
 -             if line.goods_qty > result and not line.lot_id and not self.env.context.get('wh_in_line_ids'):
 -                 # 在销售出库时如果临时缺货,自动生成一张盘盈入库单
 -                 if (line.goods_id.id, line.attribute_id.id) in goods_list:
 -                     continue
 -                 goods_list.append((line.goods_id.id, line.attribute_id.id))
 -                 all_line_message += '商品 %s ' % line.goods_id.name
 -                 if line.attribute_id:
 -                     all_line_message += ' 型号%s' % line.attribute_id.name
 -                 line_in_ids.append((0, 0, {
 -                     'goods_id': line.goods_id.id,
 -                     'attribute_id': line.attribute_id.id,
 -                     'goods_uos_qty': 0,
 -                     'uos_id': line.uos_id.id,
 -                     'goods_qty': 0,
 -                     'uom_id': line.uom_id.id,
 -                     'cost_unit': line.goods_id.cost,
 -                     'state': 'done',
 -                     'date': today}))
 -                 all_line_message += u" 当前库存量不足,继续出售请点击确定,并及时盘点库存\n"
 - 
 -             if line.goods_qty <= 0 or line.price_taxed < 0:
 -                 raise UserError('商品 %s 的数量和含税单价不能小于0。' % line.goods_id.name)
 -         if line_in_ids:
 -             vals = {
 -                 'type': 'inventory',
 -                 'warehouse_id': self.env.ref('warehouse.warehouse_inventory').id,
 -                 'warehouse_dest_id': wh_in.warehouse_id.id,
 -                 'state': 'done',
 -                 'date': today,
 -                 'line_in_ids': line_in_ids}
 -             return self.env[model_name].with_context(
 -                 {'active_model': model_name}
 -             ).open_dialog('goods_inventory', {
 -                 'message': all_line_message,
 -                 'args': [vals],
 -             })
 
 
  |