odoo销售流程代码简单剖析

来源:互联网 发布:java读取文件Textfile 编辑:程序博客网 时间:2024/05/01 08:39

odoo开发技术交流群【73934270

1、新建销售报价单


2、点击确认订单,将报价单转为销售订单,同时生成对应出库单,相关代码:

确定按钮执行的方法为 sale.order 的 action_button_confirm 方法,具体代码:

def action_button_confirm(self, cr, uid, ids, context=None):    if not context:        context = {}    assert len(ids) == 1, 'This option should only be used for a single id at a time.'    self.signal_workflow(cr, uid, ids, 'order_confirm')    if context.get('send_email'):        self.force_quotation_send(cr, uid, ids, context=context)    return True
可以看到代码有触发工作流,来确认销售订单;借住工作流视图


我们是从 draft 执行 order_confirm 进入router,此处我们打开看到,执行了 sale.order 类的 action_wait 方法,具体代码:

def action_wait(self, cr, uid, ids, context=None):#只处理自身的销售订单和产品清单的信息更新    context = context or {}    for o in self.browse(cr, uid, ids):        if not any(line.state != 'cancel' for line in o.order_line):            raise osv.except_osv(_('Error!'),_('You cannot confirm a sales order which has no line.'))        noprod = self.test_no_product(cr, uid, o, context)        if (o.order_policy == 'manual') or noprod:            self.write(cr, uid, [o.id], {'state': 'manual', 'date_confirm': fields.date.context_today(self, cr, uid, context=context)})        else:            self.write(cr, uid, [o.id], {'state': 'progress', 'date_confirm': fields.date.context_today(self, cr, uid, context=context)})        self.pool.get('sale.order.line').button_confirm(cr, uid, [x.id for x in o.order_line if x.state != 'cancel'])    return True
代码逻辑很清晰,不做进一步分析,进入 router 之后,发票部分我们暂时忽略;我们来看 wait_ship 部分,通过 sale.order 类的 procurement_needed 方法的返回值来确认程序的继续走向,具体代码:

def procurement_needed(self, cr, uid, ids, context=None):    #when sale is installed only, there is no need to create procurements, that's only    #further installed modules (sale_service, sale_stock) that will change this.    sale_line_obj = self.pool.get('sale.order.line')    res = []    for order in self.browse(cr, uid, ids, context=context):        res.append(sale_line_obj.need_procurement(cr, uid, [line.id for line in order.order_line if line.state != 'cancel'], context=context))    return any(res)
作者在测试的时候,上面的方法的返回值是True,工作流走到了 ship ,此处工作流执行的方法是 sale.order 类的  action_ship_create 方法,代码:

def action_ship_create(self, cr, uid, ids, context=None):    """Create the required procurements to supply sales order lines, also connecting    the procurements to appropriate stock moves in order to bring the goods to the    sales order's requested location.    :return: True    """    context = context or {}    context['lang'] = self.pool['res.users'].browse(cr, uid, uid).lang    procurement_obj = self.pool.get('procurement.order')    sale_line_obj = self.pool.get('sale.order.line')    for order in self.browse(cr, uid, ids, context=context):        proc_ids = []        vals = self._prepare_procurement_group(cr, uid, order, context=context)        if not order.procurement_group_id:            group_id = self.pool.get("procurement.group").create(cr, uid, vals, context=context)            order.write({'procurement_group_id': group_id})        for line in order.order_line:            if line.state == 'cancel':                continue            #Try to fix exception procurement (possible when after a shipping exception the user choose to recreate)            if line.procurement_ids:                #first check them to see if they are in exception or not (one of the related moves is cancelled)                procurement_obj.check(cr, uid, [x.id for x in line.procurement_ids if x.state not in ['cancel', 'done']])                line.refresh()                #run again procurement that are in exception in order to trigger another move                except_proc_ids = [x.id for x in line.procurement_ids if x.state in ('exception', 'cancel')]                procurement_obj.reset_to_confirmed(cr, uid, except_proc_ids, context=context)                proc_ids += except_proc_ids            elif sale_line_obj.need_procurement(cr, uid, [line.id], context=context):                if (line.state == 'done') or not line.product_id:                    continue                vals = self._prepare_order_line_procurement(cr, uid, order, line, group_id=order.procurement_group_id.id, context=context)                ctx = context.copy()                ctx['procurement_autorun_defer'] = True                proc_id = procurement_obj.create(cr, uid, vals, context=ctx)#此处执行了procurement_order表记录的生成,                proc_ids.append(proc_id)        #Confirm procurement order such that rules will be applied on it        #note that the workflow normally ensure proc_ids isn't an empty list        procurement_obj.run(cr, uid, proc_ids, context=context)        #此处run执行了stock_move表记录的生成等一系列操作,执行的run顺序===addons/stock/procurement.pyline 208        #===addons/procurement/procurement.pyline 197,stock(前者)扩展了pro(后者)的run方法        #if shipping was in exception and the user choose to recreate the delivery order, write the new status of SO        if order.state == 'shipping_except':            val = {'state': 'progress', 'shipped': False}            if (order.order_policy == 'manual'):                for line in order.order_line:                    if (not line.invoiced) and (line.state not in ('cancel', 'draft')):                        val['state'] = 'manual'                        break            order.write(val)    return True

上面代码生成与销售订单对应的补货记录,同时生成与销售订单对应的出库单,注意这里,在追代码的时候,procurement_obj.run 处调用,代码里只能看到相应的stock.move记录的生成,并没有对应出库单stock.picking的生成,一下代码是procurement_obj.run 调用的代码追踪:

def run(self, cr, uid, ids, autocommit=False, context=None):    new_ids = [x.id for x in self.browse(cr, uid, ids, context=context) if x.state not in ('running', 'done', 'cancel')]    res = super(procurement_order, self).run(cr, uid, new_ids, autocommit=autocommit, context=context)    #after all the procurements are run, check if some created a draft stock move that needs to be confirmed    #(we do that in batch because it fasts the picking assignation and the picking state computation)    move_to_confirm_ids = []    for procurement in self.browse(cr, uid, new_ids, context=context):        if procurement.state == "running" and procurement.rule_id and procurement.rule_id.action == "move":            move_to_confirm_ids += [m.id for m in procurement.move_ids if m.state == 'draft']    if move_to_confirm_ids:        self.pool.get('stock.move').action_confirm(cr, uid, move_to_confirm_ids, context=context)    return res
上面为stock模块扩展的run方法,super部分调用到父类 run 方法, 此处代码标记为1代

def run(self, cr, uid, ids, autocommit=False, context=None):#主要是变更procurement_order表的信息    for procurement_id in ids:        #we intentionnaly do the browse under the for loop to avoid caching all ids which would be resource greedy        #and useless as we'll make a refresh later that will invalidate all the cache (and thus the next iteration        #will fetch all the ids again)         procurement = self.browse(cr, uid, procurement_id, context=context)        if procurement.state not in ("running", "done"):            try:                if self._assign(cr, uid, procurement, context=context):                    res = self._run(cr, uid, procurement, context=context or {})                    #上一条 _run先调用了addons/stock/procurement.pyline 196_run方法,生成stock_move记录                    if res:                        self.write(cr, uid, [procurement.id], {'state': 'running'}, context=context)                    else:                        self.write(cr, uid, [procurement.id], {'state': 'exception'}, context=context)                else:                    self.message_post(cr, uid, [procurement.id], body=_('No rule matching this procurement'), context=context)                    self.write(cr, uid, [procurement.id], {'state': 'exception'}, context=context)                if autocommit:                    cr.commit()            except OperationalError:                if autocommit:                    cr.rollback()                    continue                else:                    raise    return True
上面代码备注的中文部分,_run 方法代码,调用到stock模块中扩展的_run(下面代码),上面代码标记为2代

def _run(self, cr, uid, procurement, context=None):    #生成stock_move表记录    if procurement.rule_id and procurement.rule_id.action == 'move':        if not procurement.rule_id.location_src_id:            self.message_post(cr, uid, [procurement.id], body=_('No source location defined!'), context=context)            return False        move_obj = self.pool.get('stock.move')        move_dict = self._run_move_create(cr, uid, procurement, context=context)        #create the move as SUPERUSER because the current user may not have the rights to do it (mto product launched by a sale for example)        move_obj.create(cr, SUPERUSER_ID, move_dict, context=context)        return True    return super(procurement_order, self)._run(cr, uid, procurement, context=context)
上面代码编辑为3代,流程分析:1代中res = super 跳入2代里执行,2代res = self._run(具体执行代码在3代里)部分执行的结果生成销售订单对应的stock.move记录,然后返回到2代再返回到1代,1代里继续执行,此处即是注意点,注意代码self.pool.get('stock.move').action_confirm,此处代码跳转到stock.move类中,picking 的生成即在stock.move 的 confirm 方法中实现的,剩下代码不做进一步详解,如有兴趣,可留言与我一起讨论分析


*以上分析为本人在学习odoo过程中的一些学习记录,如有错误请斧正,以免误导其他odoo学者

2 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 ps打开后 未响应怎么办 ps图层无法解锁怎么办 ie8浏览器电脑不能用怎么办 系统要ie6.0才能打开怎么办 2g手机内存不够怎么办 2g运行内存不够怎么办 手机运行内存2g不够怎么办 手机无法加载程序秒退怎么办 电脑账户密码忘记了怎么办 玩绝地求生卡顿怎么办 地下城总运行时间错误怎么办 逆战更新太慢怎么办 win7我的电脑没了怎么办 剑灵启动游戏慢怎么办 网页页面结束进程也关不掉怎么办 开机就启动微信怎么办 微信突然无法启动怎么办 微信发送太频繁怎么办 微信在电脑上打不开文件怎么办 微信照片电脑上打不开怎么办 换一部手机微信怎么办 微信支付宝停止运行怎么办 剑三重制版卡顿怎么办 剑三客户端更新不动了怎么办 安装包安装失败怎么办有内存 qq飞车换手机了怎么办 qq飞车求婚失败戒指怎么办 改脸型皮肤会下垂怎么办 情侣关系弄僵了怎么办 用微信交话费没有到账怎么办 微信交错话费了怎么办 微信缴费交错了怎么办 微信支付被投诉怎么办 微信q币充值错误怎么办 微信充值流量充错了怎么办 微信延迟到账怎么办 移动流量充错了怎么办 qq充话费等待发货怎么办 qq充值话费没到账怎么办 电信话费冲错了怎么办 微信手机充错话费充空号怎么办