源码阅读注解: 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
- 
兄弟干得漂亮,赞一个 
- 
mark,学习一下 

