odoo开发技术交流群【73934270】
继上一章节的销售订单,我们打开对应的出库单:
点击检查可用,执行了 stock.picking 类的 action_assign 方法,具体代码:
def action_assign(self, cr, uid, ids, context=None): """ Checks the product type and accordingly writes the state. """ import pdb;pdb.set_trace() context = context or {} quant_obj = self.pool.get("stock.quant") to_assign_moves = [] main_domain = {} todo_moves = [] operations = set() for move in self.browse(cr, uid, ids, context=context): if move.state not in ('confirmed', 'waiting', 'assigned'): continue if move.location_id.usage in ('supplier', 'inventory', 'production'): to_assign_moves.append(move.id) #in case the move is returned, we want to try to find quants before forcing the assignment if not move.origin_returned_move_id: continue if move.product_id.type == 'consu': to_assign_moves.append(move.id) continue else: todo_moves.append(move) #we always keep the quants already assigned and try to find the remaining quantity on quants not assigned only main_domain[move.id] = [('reservation_id', '=', False), ('qty', '>', 0)] #if the move is preceeded, restrict the choice of quants in the ones moved previously in original move ancestors = self.find_move_ancestors(cr, uid, move, context=context) if move.state == 'waiting' and not ancestors: #if the waiting move hasn't yet any ancestor (PO/MO not confirmed yet), don't find any quant available in stock main_domain[move.id] += [('id', '=', False)] elif ancestors: main_domain[move.id] += [('history_ids', 'in', ancestors)] #if the move is returned from another, restrict the choice of quants to the ones that follow the returned move if move.origin_returned_move_id: main_domain[move.id] += [('history_ids', 'in', move.origin_returned_move_id.id)] for link in move.linked_move_operation_ids: operations.add(link.operation_id) # Check all ops and sort them: we want to process first the packages, then operations with lot then the rest operations = list(operations) operations.sort(key=lambda x: ((x.package_id and not x.product_id) and -4 or 0) + (x.package_id and -2 or 0) + (x.lot_id and -1 or 0)) for ops in operations: #first try to find quants based on specific domains given by linked operations for record in ops.linked_move_operation_ids: move = record.move_id if move.id in main_domain: domain = main_domain[move.id] + self.pool.get('stock.move.operation.link').get_specific_domain(cr, uid, record, context=context) qty = record.qty if qty: quants = quant_obj.quants_get_prefered_domain(cr, uid, ops.location_id, move.product_id, qty, domain=domain, prefered_domain_list=[], restrict_lot_id=move.restrict_lot_id.id, restrict_partner_id=move.restrict_partner_id.id, context=context) quant_obj.quants_reserve(cr, uid, quants, move, record, context=context) for move in todo_moves: if move.linked_move_operation_ids: continue #then if the move isn't totally assigned, try to find quants without any specific domain if move.state != 'assigned': qty_already_assigned = move.reserved_availability qty = move.product_qty - qty_already_assigned quants = quant_obj.quants_get_prefered_domain(cr, uid, move.location_id, move.product_id, qty, domain=main_domain[move.id], prefered_domain_list=[], restrict_lot_id=move.restrict_lot_id.id, restrict_partner_id=move.restrict_partner_id.id, context=context) quant_obj.quants_reserve(cr, uid, quants, move, context=context) #force assignation of consumable products and incoming from supplier/inventory/production if to_assign_moves: self.force_assign(cr, uid, to_assign_moves, context=context)个人对该段代码的分析认为,根据move的去向是出库还是入库,来觉得对该条move记录是进行强制可用,还是分配库存产品,鉴于本例分析为销售订单出库,所以,本次操作需要进行库存分配,所以真正触碰到的代码是:
if move.state != 'assigned': qty_already_assigned = move.reserved_availability qty = move.product_qty - qty_already_assigned quants = quant_obj.quants_get_prefered_domain(cr, uid, move.location_id, move.product_id, qty, domain=main_domain[move.id], prefered_domain_list=[], restrict_lot_id=move.restrict_lot_id.id, restrict_partner_id=move.restrict_partner_id.id, context=context) quant_obj.quants_reserve(cr, uid, quants, move, context=context)
正常情况下此时的出库单的清单里的数量后面会显示以保留字样
点击【转移】按钮,执行的方法是 stock.picking 类的 do_enter_transfer_details 方法,代码和弹出框
@api.cr_uid_ids_context def do_enter_transfer_details(self, cr, uid, picking, context=None): if not context: context = {} context.update({ 'active_model': self._name, 'active_ids': picking, 'active_id': len(picking) and picking[0] or False }) created_id = self.pool['stock.transfer_details'].create(cr, uid, {'picking_id': len(picking) and picking[0] or False}, context) #Allen Mark: 上一句是生成临时表(暂时理解为临时表),采购订单也有这个过程 return self.pool['stock.transfer_details'].wizard_view(cr, uid, created_id, context)上面代码执行生成临时表stock.transfer_details 记录,同时生成stock.picking 对应的stock.pack.operation记录(出库单实际出库记录),编辑好出库数量,继续点击弹出框底部按钮【应用】,执行的方法为 stock.transfer_details 类的 do_detailed_transfer 方法,代码:
@api.one def do_detailed_transfer(self): processed_ids = [] # Create new and update existing pack operations for lstits in [self.item_ids, self.packop_ids]: for prod in lstits: pack_datas = { 'product_id': prod.product_id.id, 'product_uom_id': prod.product_uom_id.id, 'product_qty': prod.quantity, 'package_id': prod.package_id.id, 'lot_id': prod.lot_id.id, 'location_id': prod.sourceloc_id.id, 'location_dest_id': prod.destinationloc_id.id, 'result_package_id': prod.result_package_id.id, 'date': prod.date if prod.date else datetime.now(), 'owner_id': prod.owner_id.id, } if prod.packop_id: prod.packop_id.with_context(no_recompute=True).write(pack_datas) processed_ids.append(prod.packop_id.id) else: pack_datas['picking_id'] = self.picking_id.id packop_id = self.env['stock.pack.operation'].create(pack_datas) processed_ids.append(packop_id.id) #以上代码就是在点击转移的时候弹出界面(该界面表是中转表stock.transfer_details),供用户修改数据,并回填给stock_pack_operation表,stock_picking方法do_transfer()使用 # Delete the others packops = self.env['stock.pack.operation'].search(['&', ('picking_id', '=', self.picking_id.id), '!', ('id', 'in', processed_ids)]) packops.unlink() # Execute the transfer of the picking self.picking_id.do_transfer() return True
上面代码执行完毕,一切正常的话,该出库单流程结束
有几张图因为csdn上传图片失败,后续正常后补上