Odoo中文社区可以通过以下三个域名访问:shine-it.net , odoocn.org,odoo.net.cn

原论坛用户的基本信息和发帖这里都予以保留,请注意:原论坛用户无需重新注册新用户,但是您的密码需要重置

开发人员可以登录gitter讨论组: http://gitter.im/odoo-china/Talk, 需要github账号

如果您登录系统碰到问题,请在微信公众号留言:

源码阅读注解: sale.py 中 product_id_change 函数



  • 今天读了一下 sale.py 中 product_id_change 函数,

    水平不高, 读得累, 花了2小时

    笔记如下, 一起学习 :

    ===  视图中调用的定义  ==

        <field name="product_id"
    context="{'partner_id':parent.partner_id, 'quantity':product_uom_qty, 'pricelist':parent.pricelist_id, 'shop':parent.shop_id, 'uom':product_uom}"
    groups="base.group_user"
    on_change="product_id_change(parent.pricelist_id, product_id, product_uom_qty, product_uom, product_uos_qty, product_uos, name,

    parent.partner_id, False, True, parent.date_order, False, parent.fiscal_position, False, context)"/>

    parent.partner_id
    parent.date_order

    这种用法, 我估计是 取 得当前 的 one2many 字段所在 form的 其它字段 的值
       
    = 以下为  py 中的对应函数  ===============================================================================================================

        def product_id_change(self, cr, uid, ids, pricelist, product, qty=0,
                uom=False, qty_uos=0, uos=False, name='', partner_id=False,
                lang=False, update_tax=True, date_order=False, packaging=False, fiscal_position=False, flag=False, context=None):
            context = context or {}
            lang = lang or context.get('lang',False)
           
    # 判断, 选择产品前,必须先选择  客户
    if not  partner_id:
                raise osv.except_osv(_('No Customer Defined !'), _('Before choosing a product,\n select a customer in the sales form.'))


    warning = {}
            product_uom_obj = self.pool.get('product.uom')
            partner_obj = self.pool.get('res.partner')
            product_obj = self.pool.get('product.product')


    # 准备好 context , 包括 语言 和 客户ID
    context = {'lang': lang, 'partner_id': partner_id}
    if partner_id:
                lang = partner_obj.browse(cr, uid, partner_id).lang
    # 准备第二个 context, 语言 改为 客户的 lang
            context_partner = {'lang': lang, 'partner_id': partner_id}



    # 如果将 产品选中为 空(即 清空产品后), 重量 置0 , 产品销售单位数量 设为 qyt ,  产品单位, 产品小数单位 的 domain 清空, 返回
            if not product:
                return {'value': {'th_weight': 0,
                    'product_uos_qty': qty}, 'domain': {'product_uom': [],
                      'product_uos': []}}

    # 如果订单尚未指定 订单日期, 则 取得默认的 日期, 字串类型str
            if not date_order:
                date_order = time.strftime(DEFAULT_SERVER_DATE_FORMAT)

            result = {}
            warning_msgs = {}

            # 如果传入的 product(=product_id) 有值, 则打开该 ID 对应的对象
    product_obj = product_obj.browse(cr, uid, product, context=context_partner)

           
    ######### 下面开始处理单位

    uom2 = False
            if uom:
        # 根据 uom 取得单位的 对象
                uom2 = product_uom_obj.browse(cr, uid, uom)

        # 如果 产品默认的 计量单位的类别和 输入参数中单位的 类别不一致,
                if product_obj.uom_id.category_id.id != uom2.category_id.id:
    uom = False  # 将 传入的单位设置为 空



            if uos:
                if product_obj.uos_id:
                    uos2 = product_uom_obj.browse(cr, uid, uos)
                    if product_obj.uos_id.category_id.id != uos2.category_id.id:
                        uos = False # 如果产品的销售单位 跟 输入参数的销售单位 类别不一致, , 也将 传入的 销售单位置空
                else:
                    uos = False # 如果 产品的 销售单位 未指定, 此处的销售单位也必须为空


    # fiscal_position 财务替换规则
    # 根据 ID 取得 财务替换规格的 对象
            fpos = fiscal_position and self.pool.get('account.fiscal.position').browse(cr, uid, fiscal_position) or False

    # 根据 财务替换规则 和 产品的 税务设定 进行 映射
    if update_tax: #The quantity only have changed
                result['tax_id'] = self.pool.get('account.fiscal.position').map_tax(cr, uid, fpos, product_obj.taxes_id)



    # flag 是个标识 ,因为 本函数 是多个字段 定义为对应的 onchange 函数, 某些 字段的 onchange 会输入参数  flag = True
            if not flag:
                result['name'] = self.pool.get('product.product').name_get(cr, uid, [product_obj.id], context=context_partner)[0][1]    # 这里的  context_partner 已经包含了客户的语言 , 取得输入产品的 名称(根据客户的语言翻译好)
                if product_obj.description_sale:    # 如果产品里设置了 销售说明字段 , 则添加上
                    result['name'] += '\n'+product_obj.description_sale      #  等价于  result['name'] =  result['name'] + '\n'+product_obj.description_sale

            domain = {}

           

    #  根据前面的单位 计算  销售单位的数量  以及  单位字段的 domain



    if (not uom) and (not uos): # 如果 计量单位和销售单位 都 无效 (可能是 没输入,也可能 是被前面 检查后置空了)
                result['product_uom'] = product_obj.uom_id.id

        if product_obj.uos_id: # 产品中指定了销售单位
                    result['product_uos'] = product_obj.uos_id.id # 从产品取 销售单位
                    result['product_uos_qty'] = qty * product_obj.uos_coeff # 根据 转换系数 和 输入的 数量 计算 销售单位对应数量
                    uos_category_id = product_obj.uos_id.category_id.id # 销售单位的类别 ID, 用于 后面的 对 销售单位设置 domain
                else:
                    result['product_uos'] = False # 产品中没有 销售单位, 则销售单位置空
                    result['product_uos_qty'] = qty # 销售单位数量 = 输入的数量(计量单位对应数量)
                    uos_category_id = False # 销售单位的类别 ID, 用于 设置 domain, 设为 false 将导致 销售单位 无法选择

                result['th_weight'] = qty * product_obj.weight # 计算本行产品的重量 =  数量 * 单个重量
                domain = {'product_uom':
                            [('category_id', '=', product_obj.uom_id.category_id.id)], # 修改 计量单位的 product_uom 的 domain , 仅允许同类单位可选
                            'product_uos':
                            [('category_id', '=', uos_category_id)]} # 同上



            elif uos and not uom: # only happens if uom is False # 销售单位有效,  计量单位无效
                result['product_uom'] = product_obj.uom_id and product_obj.uom_id.id # 取得产品中设置的  计量单位
                result['product_uom_qty'] = qty_uos / product_obj.uos_coeff # 根据 销售单位数量 / 系数  , 倒算出 计量单位数量
                result['th_weight'] = result['product_uom_qty'] * product_obj.weight # 计算重量 计量单位数量 * 单个产品重量



            elif uom: # whether uos is set or not # 计量单位有效 , 销售单位 不确定

                default_uom = product_obj.uom_id and product_obj.uom_id.id # 默认计量单位 从 产品中取得
                q = product_uom_obj._compute_qty(cr, uid, uom, qty, default_uom) # 根据输入单位 , 输入的数量 以及 产品的默认计量单位 , 计算数量(根据单位的比例关系进行转换)
                result['product_uom'] = default_uom # 返回默认单位

                if product_obj.uos_id:
                    result['product_uos'] = product_obj.uos_id.id # 如果产品设置了  销售单位 , 则从产品 取得 销售单位
                    result['product_uos_qty'] = qty * product_obj.uos_coeff # 根据输入数量和 转换系数计算出 产品的销售单位 数量
                else:
                    result['product_uos'] = False # 产品 没有设置 销售单位
                    result['product_uos_qty'] = qty # 销售单位数量 直接 设为 输入数量
                result['th_weight'] = q * product_obj.weight        # Round the quantity up # 重量 为 折算的单个产品数量 * 单个产品的重量  ( 产品设置的默认计量单位和 产品行的输入的计量单位不一致时, 前面已经折算)

            if not uom2: # uom2 是前面 检查 uom 时 取得的 uom 对应的 对象,
                uom2 = product_obj.uom_id # 如果前面 uom 尚未取得值, 这里就直接取得 产品对象中 所属 的 uom 对象



            # get unit price
    # 下面 开始 取得单价

            if not pricelist: 
                warn_msg = _('You have to select a pricelist or a customer in the sales form !\n' #  没有指定 价格表, 抛错
                        'Please set one before choosing a product.')
                warning_msgs += _("No Pricelist ! : ") + warn_msg +"\n\n"
            else:
                price = self.pool.get('product.pricelist').price_get(cr, uid, [pricelist], # 根据产品, 数量, 客户 , 计量单位, 订单日期 等信息 ( price_get 待 进一步 解读)
                        product, qty or 1.0, partner_id, {
                            'uom': uom or result.get('product_uom'),
                            'date': date_order,
                            })[pricelist] # price_get 返回一个以 pricelist 为 key 的 字典, 包含价格

                if price is False:
                    warn_msg = _("Cannot find a pricelist line matching this product and quantity.\n" # 如果 取得的 price 失败, 抛错
                            "You have to change either the product, the quantity or the pricelist.")

                    warning_msgs += _("No valid pricelist line found ! :") + warn_msg +"\n\n"
                else:
                    result.update({'price_unit': price}) #  取得 单价

            if warning_msgs: # 如果前面的 计算过程 包括 了警告信息,这 构造一个 字典予以返回
                warning = {
                          'title': _('Configuration Error!'),
                          'message' : warning_msgs
                        }
            return {'value': result, 'domain': domain, 'warning': warning} # onchang 函数 返回 三个字典:
    # 'value': result, 修改的字段值, 以字段名为 key
    # 'domain': domain, 也是以 字段名为key
    # 'warning': warning



  • 今天读了一下 sale.py 中 product_id_change 函数,

    水平不高, 读得累, 花了2小时

    笔记如下, 一起学习 :

    ===  视图中调用的定义  ==

        <field name="product_id"
    context="{'partner_id':parent.partner_id, 'quantity':product_uom_qty, 'pricelist':parent.pricelist_id, 'shop':parent.shop_id, 'uom':product_uom}"
    groups="base.group_user"
    on_change="product_id_change(parent.pricelist_id, product_id, product_uom_qty, product_uom, product_uos_qty, product_uos, name,

    parent.partner_id, False, True, parent.date_order, False, parent.fiscal_position, False, context)"/>

    parent.partner_id
    parent.date_order

    这种用法, 我估计是 取 得当前 的 one2many 字段所在 form的 其它字段 的值
       
    = 以下为  py 中的对应函数  ===============================================================================================================

        def product_id_change(self, cr, uid, ids, pricelist, product, qty=0,
                uom=False, qty_uos=0, uos=False, name='', partner_id=False,
                lang=False, update_tax=True, date_order=False, packaging=False, fiscal_position=False, flag=False, context=None):
            context = context or {}
            lang = lang or context.get('lang',False)
           
    # 判断, 选择产品前,必须先选择  客户
    if not  partner_id:
                raise osv.except_osv(_('No Customer Defined !'), _('Before choosing a product,\n select a customer in the sales form.'))


    warning = {}
            product_uom_obj = self.pool.get('product.uom')
            partner_obj = self.pool.get('res.partner')
            product_obj = self.pool.get('product.product')


    # 准备好 context , 包括 语言 和 客户ID
    context = {'lang': lang, 'partner_id': partner_id}
    if partner_id:
                lang = partner_obj.browse(cr, uid, partner_id).lang
    # 准备第二个 context, 语言 改为 客户的 lang
            context_partner = {'lang': lang, 'partner_id': partner_id}



    # 如果将 产品选中为 空(即 清空产品后), 重量 置0 , 产品销售单位数量 设为 qyt ,  产品单位, 产品小数单位 的 domain 清空, 返回
            if not product:
                return {'value': {'th_weight': 0,
                    'product_uos_qty': qty}, 'domain': {'product_uom': [],
                      'product_uos': []}}

    # 如果订单尚未指定 订单日期, 则 取得默认的 日期, 字串类型str
            if not date_order:
                date_order = time.strftime(DEFAULT_SERVER_DATE_FORMAT)

            result = {}
            warning_msgs = {}

            # 如果传入的 product(=product_id) 有值, 则打开该 ID 对应的对象
    product_obj = product_obj.browse(cr, uid, product, context=context_partner)

           
    ######### 下面开始处理单位

    uom2 = False
            if uom:
        # 根据 uom 取得单位的 对象
                uom2 = product_uom_obj.browse(cr, uid, uom)

        # 如果 产品默认的 计量单位的类别和 输入参数中单位的 类别不一致,
                if product_obj.uom_id.category_id.id != uom2.category_id.id:
    uom = False  # 将 传入的单位设置为 空



            if uos:
                if product_obj.uos_id:
                    uos2 = product_uom_obj.browse(cr, uid, uos)
                    if product_obj.uos_id.category_id.id != uos2.category_id.id:
                        uos = False # 如果产品的销售单位 跟 输入参数的销售单位 类别不一致, , 也将 传入的 销售单位置空
                else:
                    uos = False # 如果 产品的 销售单位 未指定, 此处的销售单位也必须为空


    # fiscal_position 财务替换规则
    # 根据 ID 取得 财务替换规格的 对象
            fpos = fiscal_position and self.pool.get('account.fiscal.position').browse(cr, uid, fiscal_position) or False

    # 根据 财务替换规则 和 产品的 税务设定 进行 映射
    if update_tax: #The quantity only have changed
                result['tax_id'] = self.pool.get('account.fiscal.position').map_tax(cr, uid, fpos, product_obj.taxes_id)



    # flag 是个标识 ,因为 本函数 是多个字段 定义为对应的 onchange 函数, 某些 字段的 onchange 会输入参数  flag = True
            if not flag:
                result['name'] = self.pool.get('product.product').name_get(cr, uid, [product_obj.id], context=context_partner)[0][1]    # 这里的  context_partner 已经包含了客户的语言 , 取得输入产品的 名称(根据客户的语言翻译好)
                if product_obj.description_sale:    # 如果产品里设置了 销售说明字段 , 则添加上
                    result['name'] += '\n'+product_obj.description_sale      #  等价于  result['name'] =  result['name'] + '\n'+product_obj.description_sale

            domain = {}

           

    #  根据前面的单位 计算  销售单位的数量  以及  单位字段的 domain



    if (not uom) and (not uos): # 如果 计量单位和销售单位 都 无效 (可能是 没输入,也可能 是被前面 检查后置空了)
                result['product_uom'] = product_obj.uom_id.id

        if product_obj.uos_id: # 产品中指定了销售单位
                    result['product_uos'] = product_obj.uos_id.id # 从产品取 销售单位
                    result['product_uos_qty'] = qty * product_obj.uos_coeff # 根据 转换系数 和 输入的 数量 计算 销售单位对应数量
                    uos_category_id = product_obj.uos_id.category_id.id # 销售单位的类别 ID, 用于 后面的 对 销售单位设置 domain
                else:
                    result['product_uos'] = False # 产品中没有 销售单位, 则销售单位置空
                    result['product_uos_qty'] = qty # 销售单位数量 = 输入的数量(计量单位对应数量)
                    uos_category_id = False # 销售单位的类别 ID, 用于 设置 domain, 设为 false 将导致 销售单位 无法选择

                result['th_weight'] = qty * product_obj.weight # 计算本行产品的重量 =  数量 * 单个重量
                domain = {'product_uom':
                            [('category_id', '=', product_obj.uom_id.category_id.id)], # 修改 计量单位的 product_uom 的 domain , 仅允许同类单位可选
                            'product_uos':
                            [('category_id', '=', uos_category_id)]} # 同上



            elif uos and not uom: # only happens if uom is False # 销售单位有效,  计量单位无效
                result['product_uom'] = product_obj.uom_id and product_obj.uom_id.id # 取得产品中设置的  计量单位
                result['product_uom_qty'] = qty_uos / product_obj.uos_coeff # 根据 销售单位数量 / 系数  , 倒算出 计量单位数量
                result['th_weight'] = result['product_uom_qty'] * product_obj.weight # 计算重量 计量单位数量 * 单个产品重量



            elif uom: # whether uos is set or not # 计量单位有效 , 销售单位 不确定

                default_uom = product_obj.uom_id and product_obj.uom_id.id # 默认计量单位 从 产品中取得
                q = product_uom_obj._compute_qty(cr, uid, uom, qty, default_uom) # 根据输入单位 , 输入的数量 以及 产品的默认计量单位 , 计算数量(根据单位的比例关系进行转换)
                result['product_uom'] = default_uom # 返回默认单位

                if product_obj.uos_id:
                    result['product_uos'] = product_obj.uos_id.id # 如果产品设置了  销售单位 , 则从产品 取得 销售单位
                    result['product_uos_qty'] = qty * product_obj.uos_coeff # 根据输入数量和 转换系数计算出 产品的销售单位 数量
                else:
                    result['product_uos'] = False # 产品 没有设置 销售单位
                    result['product_uos_qty'] = qty # 销售单位数量 直接 设为 输入数量
                result['th_weight'] = q * product_obj.weight        # Round the quantity up # 重量 为 折算的单个产品数量 * 单个产品的重量  ( 产品设置的默认计量单位和 产品行的输入的计量单位不一致时, 前面已经折算)

            if not uom2: # uom2 是前面 检查 uom 时 取得的 uom 对应的 对象,
                uom2 = product_obj.uom_id # 如果前面 uom 尚未取得值, 这里就直接取得 产品对象中 所属 的 uom 对象



            # get unit price
    # 下面 开始 取得单价

            if not pricelist: 
                warn_msg = _('You have to select a pricelist or a customer in the sales form !\n' #  没有指定 价格表, 抛错
                        'Please set one before choosing a product.')
                warning_msgs += _("No Pricelist ! : ") + warn_msg +"\n\n"
            else:
                price = self.pool.get('product.pricelist').price_get(cr, uid, [pricelist], # 根据产品, 数量, 客户 , 计量单位, 订单日期 等信息 ( price_get 待 进一步 解读)
                        product, qty or 1.0, partner_id, {
                            'uom': uom or result.get('product_uom'),
                            'date': date_order,
                            })[pricelist] # price_get 返回一个以 pricelist 为 key 的 字典, 包含价格

                if price is False:
                    warn_msg = _("Cannot find a pricelist line matching this product and quantity.\n" # 如果 取得的 price 失败, 抛错
                            "You have to change either the product, the quantity or the pricelist.")

                    warning_msgs += _("No valid pricelist line found ! :") + warn_msg +"\n\n"
                else:
                    result.update({'price_unit': price}) #  取得 单价

            if warning_msgs: # 如果前面的 计算过程 包括 了警告信息,这 构造一个 字典予以返回
                warning = {
                          'title': _('Configuration Error!'),
                          'message' : warning_msgs
                        }
            return {'value': result, 'domain': domain, 'warning': warning} # onchang 函数 返回 三个字典:
    # 'value': result, 修改的字段值, 以字段名为 key
    # 'domain': domain, 也是以 字段名为key
    # 'warning': warning


  • 管理员

    兄弟干得漂亮,赞一个



  • 请教一个问题:
    要实现图中所示的,如何实现:在改变产品ID后,显示产品在手库存数?是否修改product_id_change,如何改?请指教。
    -------------
    已在sale.order.line模块中添加了字段x_qty_available (标签Quantity On Hand)。



  • 楼上的需求需要写 python代码的



  • 能帮助写代码吗?



  • mark,学习一下



  • 学习!



  • 学习,很有帮助,谢谢


登录后回复
 

与 Odoo 中文社区 的连接断开,我们正在尝试重连,请耐心等待