今天开始OpenERP进销存源码分析活动
-
HornERP3.0的目标是针对OpenERP的核心进销存模块做增强和简化。那么前提就是对系统原有的功能和逻辑有深入了解,为了满足这个前提,HornERP项目组决定从今天(2010-9-1)开始,对OpenERP5.0.12版本的stock、purchase、sale、mrp等进销存涉及的基础模块做源码级的研究和分析。为保持开源项目的开放性,此活动以下面方式运作:
1、每天此帖公布当天分析的对象和次日分析的计划
2、分析和讨论在qq群中进行,聊天记录整理后记录到此帖
3、根据分析结果完善中文帮助的wiki
4、根据分析结果完善各模块对应的翻译
5、根据分析结果总结出HornERP3.0对应的需求
愿意参与的,有更好的运作建议的,已经有心得的,马上加入我们!! -
研究ERP流程的惯例是先主数据后业务数据,那就从partner开始吧
http://code.google.com/p/hornerp/source/browse/branches/codereview/server/5.0.12/bin/addons/base/res/partner/partner.py -
上海-Jeff(85822082) 15:03:51
partner是base模块里就带着的一个业务对象,基本上每个后续模块都会继承base里的这个partner对象,往上加字段,所以今天我们针对业务功能的源码分析就从这个开始
打开这个
[attachimg=1]
只有base模块的database,partner就是这些字段对应网页上的源文件 130 行其他class都是为它服务的,逐个字段分析res_partner对象就能都涉及到class res_partner(osv.osv): 所有oe的业务对象类都是osv的子类_description='Partner' 这就是在对象列表里显示的名称,每个类都必须有 _name = "res.partner"
上海-Jeff(85822082) 15:11:12
_order = "name" ,这里要注意了,partnr列表是按名称排序的,不是我们习惯的按编号
好像列表的排序只能在列里定义,view里无法确定顺序?
广东-Joshua(470534800) 15:13:06
view没试过
上海-Jeff(85822082) 15:14:23
_columns = {
这个也是必需的
'name': fields.char('Name', size=128, required=True, select=True),
广东-Joshua(470534800) 15:20:22
'name': fields.char('Name', size=128, required=True, select=True),
select=False, 如为True,在数据库表的这个字段上创建一个索引
上海-Jeff(85822082) 15:21:10
我也想问一下这个select,真的是用来创建索引的?
深圳-Seraphim(409166304) 3:17:10 PM
mrp\mrp_view.xml 474行,加个groups试试
广东-Joshua(470534800) 15:22:32
'date': fields.date('Date', select=1),第二条 =1..
上海-Jeff(85822082) 15:23:21
是啊,怪就怪在他一会儿是True False,一会儿是1,2
广东-Joshua(470534800) 15:23:55
[attachimg=2]
上海-Jeff(85822082) 15:25:26
确实建了三个索引
广东-Joshua(470534800) 15:28:20
分别是 true 1 2
重庆-mrshelly(49812643) 15:55:13
关于 selected 的. 不是 为2就是高级检索吗? 0 不放到检索里. 1 放到 base 检索里. 2 放到advance 检索里.
上海-Jeff(85822082) 15:55:46
true呢?
重庆-mrshelly(49812643) 15:55:49
true=1
False = 0
上海-Jeff(85822082) 15:57:06
你说的这个好象是view.xml里的select属性
重庆-mrshelly(49812643) 15:57:09
字段的这个也是一样的.
上海-Jeff(85822082) 15:57:26
我们刚才说的是class里field的select属性
重庆-mrshelly(49812643) 15:57:28
是啊. 你view 里没有指定的话. 就是 field 的 select 值啊. 就如同 digits 在字段里指定了.
上海-Jeff(85822082) 15:57:59
[attachimg=3]
广东-Joshua(470534800) 15:58:32
没有date
上海-Jeff(85822082) 15:59:16
没有parent_id哦,parent_id在高级里,=2
广东-Joshua(470534800) 16:00:36
'date': fields.date('Date', select=1),
上海-Jeff(85822082) 16:01:23
接下来去看看view里是不是去掉了date,加了ref customer contact
<field name="name" select="1"/>
<field name="ref" select="1"/>
<field name="customer" select="1"/>
上海-Jeff(85822082) 16:02:58
<field name="date" select="2"/>
广东-Joshua(470534800) 16:03:41
[attachimg=4]
广东-Joshua(470534800) 16:04:03
[attachimg=5]
上海-Jeff(85822082) 15:34:59
'title': fields.selection(_partner_title_get, 'Title', size=32),
这里用到了动态下拉列表
为了过滤 partner_title的类型。因为那个title可能是给个人用的也可能是给公司用的,这里下拉列表只显示可供公司用的title
上海-Jeff(85822082) 15:41:24
'child_ids': fields.one2many('res.partner', 'parent_id', 'Partner Ref.'),
既然有父公司,就有子公司,这个字段是给那个公司结构的tree用的
上海-Jeff(85822082) 15:49:20
'lang': fields.selection(_lang_get, 'Language', size=5, help="If the selected language is loaded in the system, all documents related to this partner will be printed in this language. If not, it will be english."),
又一个动态下拉列表,只显示已安装的语言
发现这种用法那个_lang_get 都是class之外的function啊,不知道另一个class的method行不行?
'user_id': fields.many2one('res.users', 'Dedicated Salesman', help='The internal user that is in charge of communicating with this partner if any.'),
上海-Jeff(85822082) 15:53:15
这个是负责这个业务伙伴的用户,叫客户经理?专员?
上海-Jeff(85822082) 15:53:45
业务伙伴专员?
粤-Black Jack(104144648) 15:54:05
业务员
四川--Tomp(139776) 15:53:46
都差不多的
上海-Jeff(85822082) 15:54:24
现在的翻译是业务专员,感觉业务员更好些,呵呵
上海-Jeff(85822082) 16:08:29
'bank_ids': fields.one2many('res.partner.bank', 'partner_id', 'Banks'),
这个界面上也没有啊,估计都是给account服务的
四川--Tomp(139776) 16:08:46
还不如增加银行账户更加实用
上海-Jeff(85822082) 16:10:00
要不要跳到res.partner.bank去看看?
上海-Jeff(85822082) 16:13:43
'''Bank Accounts'''
_description = doc ,玩这个花样干嘛
_name = "res.partner.bank"
_rec_name = "acc_number"
_rec_name
名称字段的字段名,默认是"name"。这个字段的字段值是类的name_get的返回值。many2one的字段在界面上的文本框里显示的就是这个值。
这个挺有用的
重庆-mrshelly(49812643) 16:16:45
最多的应用 就象 科目 名.
上海-Jeff(85822082) 16:16:48
_order = 'sequence'
不按银行帐号排序却按sequence排序,有趣
joshua发的那个截屏,哪有sequence
重庆-mrshelly(49812643) 16:18:16
然后 把经常要操作的银行记录调到最前面.
上海-Jeff(85822082) 16:18:38
按一个不在界面上显示的字段排序,这个有点妖异吧
重庆-mrshelly(49812643) 16:18:50
排序应该只针对 tree view
重庆-mrshelly(49812643) 16:22:07
change_default 是玩啥的?
上海-Jeff(85822082) 16:22:21
让我疑惑的是为什么不弄个address,还搞那些国省市街的干啥
重庆-mrshelly(49812643) 16:22:57
我估计这样精确定位后, 可以确定汇率, 以及计算手续费.
重庆-mrshelly(49812643) 16:23:29
然后 匹配 离银行最近的公司.
重庆-mrshelly(49812643) 16:23:37
或者离某公司最近的银行.
上海-Jeff(85822082) 16:23:40
change_default=False, 该字段可以作为取其他字段的当前用户默认值的条件
重庆-mrshelly(49812643) 16:24:08
True 就每次修改都把最后修改当默认值?
上海-Jeff(85822082) 16:25:05
你存默认值的时候不止可以存 B field的value,还可以存这个A field作为条件,你要是常用用户默认值就知道了
重庆-mrshelly(49812643) 16:26:03
按我的想法. 好象是说, change_default 就是改变默认值.
上海-Jeff(85822082) 16:26:13
你save过默认值么?没事儿试试,有change_default=True和没有change_default=True会是不同的精彩
重庆-mrshelly(49812643) 16:26:51
[attachimg=10]
两个有什么区别?
上海-Jeff(85822082) 16:27:18
上面是get,下面是set
上海-Jeff(85822082) 16:30:21
我找到了一个例子
上海-Jeff(85822082) 16:30:14
[attachimg=6]
重庆-mrshelly(49812643) 16:30:36
我打开保存, 还是没有能理解.
上海-Jeff(85822082) 16:30:59
这里这个邮编就是 change_default=True
广东-Joshua(470534800) 16:31:09
联动
重庆-mrshelly(49812643) 16:31:31
那联动条件呢?
上海-Jeff(85822082) 16:31:51
change_default=True的字段就是联动条件
重庆-mrshelly(49812643) 16:32:20
哦?? 意思是 change_default 可以是 True False 也可以是一个 domain?
上海-Jeff(85822082) 16:33:47
应该只是是boolean吧
重庆-mrshelly(49812643) 16:34:09
上海-Jeff(85822082) 16:34:14
就是那个 值有效如果...
重庆-mrshelly(49812643) 16:34:20
这个 zip = 313200 在哪里设置的?
广东-Joshua(470534800) 16:34:49
[attachimg=7]
重庆-mrshelly(49812643) 16:34:57
难道是 公司定义的 zip?
广东-Joshua(470534800) 16:35:00
你填神马就是神马
上海-Jeff(85822082) 16:35:04
213200是你在存默认值的时候zip字段的值啊
重庆-mrshelly(49812643) 16:35:28
那 change_default 倒底是定义到 zip 上?
重庆-mrshelly(49812643) 16:35:35
还是定义在 Function 字段上?
上海-Jeff(85822082) 16:35:40
是在zip上的
重庆-mrshelly(49812643) 16:35:49
那就是说. 如果一个字段定义了.
上海-Jeff(85822082) 16:35:58
change_default=True就可以给人服务
重庆-mrshelly(49812643) 16:36:20
如果 country也定义了 change_default=True
那么, 在 Function 里设置默认值就会有两个条件/
上海-Jeff(85822082) 16:36:50
会有两个check box
重庆-mrshelly(49812643) 16:36:41
那么, 在 Function 里设置默认值就会有两个条件/
上海-Jeff(85822082) 16:36:50
会有两个check box
上海-Jeff(85822082) 16:39:18
我这里出不来两个checkbox
重庆-mrshelly(49812643) 16:39:35
那出来哪一个? 还是 zip? 你把 zip 的定义调后一点...
上海-Jeff(85822082) 16:39:53
一个都不出来,估计是bug
重庆-mrshelly(49812643) 16:39:56
看看 会不会出来 country. 好多字段都是 change_default
==========================================
重庆-mrshelly(49812643) 16:40:41
看这个:
[attachimg=8]
广东-Joshua(470534800) 16:42:26
那个联动的我先在好像测试不到他有动,明明记得之前测试时可以的
重庆-mrshelly(49812643) 16:42:55
[attachimg=9]
上海-Jeff(85822082) 16:43:09
这个用来写文章,当然,大多数时候都是空着的
重庆-mrshelly(49812643) 16:43:08
bank 多个 change_default 的确没用.
============================================================
上海-Jeff(85822082) 16:40:05
下一话题,回到partner:
'website': fields.char('Website',size=64),
这个牛B的地方在于它的view做成了一个link
上海-Jeff(85822082) 16:42:53
'comment': fields.text('Notes')
'address': fields.one2many('res.partner.address', 'partner_id', 'Contacts'),
配角上场,这个复杂了,必须跳到res.partner.address上去了
_description ='Partner Addresses'
_name = 'res.partner.address'
_order = 'id'
_columns = {
重庆-mrshelly(49812643) 16:44:52
就是联系人嘛..
广东-Joshua(470534800) 16:45:10
我不明白为什么
'street': fields.char('Street', size=128),
'street2': fields.char('Street2', size=128),
2个地址!
上海-Jeff(85822082) 16:46:07
怕128位写不下,再搞个延长的
重庆-mrshelly(49812643) 16:46:24
那还不如用 256
四川--Tomp(139776) 16:46:19
太长了影响试图
上海-Jeff(85822082) 16:45:45
'partner_id': fields.many2one('res.partner', 'Partner', ondelete='set null', select=True, help="Keep empty for a private address, not related to partner."),
广东-Joshua(470534800) 16:46:27
ondelete="setnull", 用于many2one类型的字段 如setnull,one的对象删除后many这边字段值设为空
上海-Jeff(85822082) 16:46:49
就是说删了业务伙伴不删地址
重庆-mrshelly(49812643) 16:46:48
阔以写 "SetNULL" 不?
上海-Jeff(85822082) 16:47:30
前面注意了没有,删除业务伙伴bank也会被删
广东-Joshua(470534800) 16:47:39
@shelly ondelete='set null'
上海-Jeff(85822082) 16:48:02
同样是many2one,差距可大了,写程序的时候可注意了
广东-Joshua(470534800) 16:48:39
'type': fields.selection( [ ('default','Default'),('invoice','Invoice'), ('delivery','Delivery'), ('contact','Contact'), ('other','Other') ],'Address Type', help="Used to select automatically the right address according to the context in sales and purchases documents."),
上海-Jeff(85822082) 16:49:48
这里就有意思了,每个公司必须有个default
上海-Jeff(85822082) 16:50:17
inv del两个有也行,没有就用default填充
上海-Jeff(85822082) 16:50:29
contact只是其中一个类型
重庆-mrshelly(49812643) 16:50:39
没有看到必须有 default 呀.
上海-Jeff(85822082) 16:51:08
没有的话采购订单和销售订单都取不到地址
上海-Jeff(85822082) 16:51:52
我曾经建议一个网商设置一个没有default的partner作为零售客户
上海-Jeff(85822082) 16:52:06
因为每次送货地址都变化
重庆-mrshelly(49812643) 16:52:36
难道, 只建一个 partner ? 然后 每个客户一个地址?
广东-Joshua(470534800) 16:52:42
sales order report 里面用到这个
上海-Jeff(85822082) 16:52:45
有个小子昨天说每个公司都至少有个联系人,请看昨天下午的聊天记录
地址是根据partner复制到order的
上海-Jeff(85822082) 16:53:51
不需要——然后 每个客户一个地址?
上海-Jeff(85822082) 16:54:16
这个我们看order的代码的时候会看到
上海-Jeff(85822082) 16:54:34
'function': fields.many2one('res.partner.function', 'Function'),
广东-Joshua(470534800) 16:55:21
业务伙伴联系人的职务,职能
上海-Jeff(85822082) 16:55:29
职位,没啥说的
广东-Joshua(470534800) 16:55:41
职位的code不能重复
上海-Jeff(85822082) 16:55:40
'title': fields.selection(_contact_title_get, 'Title', size=32),
动态下拉菜单,只能选针对个人的称谓,雷德森或者健特们
上海-Jeff(85822082) 16:57:08
'name': fields.char('Contact Name', size=64),
'street': fields.char('Street', size=128),
'street2': fields.char('Street2', size=128),
'zip': fields.char('Zip', change_default=True, size=24),
'city': fields.char('City', size=128),
'state_id': fields.many2one("res.country.state", 'Fed. State', domain="[('country_id','=',country_id)]"),
广东-Joshua(470534800) 16:57:08
def _contact_title_get(self, cr, uid, context={}):
obj = self.pool.get('res.partner.title')
ids = obj.search(cr, uid, [('domain', '=', 'contact')])
res = obj.read(cr, uid, ids, ['shortcut','name'], context)
return [(r['shortcut'], r['name']) for r in res] + [('','')]
重庆-mrshelly(49812643) 16:57:07
好多定义里都有类似的代码.
上海-Jeff(85822082) 16:57:17
这才是联动
上海-Jeff(85822082) 16:57:38
domain="[('country_id','=',country_id)]"),
重庆-mrshelly(49812643) 16:57:48
这个是过滤呀...
上海-Jeff(85822082) 16:58:14
'birthdate': fields.char('Birthdate', size=64),
重庆-mrshelly(49812643) 16:58:14
联动, 我觉得 是 onchange
上海-Jeff(85822082) 16:58:21
为啥不用date?
上海-Jeff(85822082) 16:58:52
'active': fields.boolean('Active', help="Uncheck the active field to hide the contact."),
四川--Tomp(139776) 17:00:30
_defaults = {
'active': lambda *a: 1,
}
有缺省值 -
上次说到298行,birthdate
*
上海-Jeff (85822082)
2010-09-06 15:16:50
'active': fields.boolean('Active', help="Uncheck the active field to hide the contact."),
*
上海-Jeff (85822082)
2010-09-06 15:17:21
这个active是个很特殊的字段,是orm里默认的五个字段之一
*
上海-Jeff (85822082)
2010-09-06 15:18:37
一个对象至少包含这5个字段,即使你不在_columns里指定,pgsql表里也会创建
*
上海-Jeff (85822082)
2010-09-06 15:19:26
但是一旦你需要把它显示在view里,那就必须在对象的_columns属性里显式地声明他
*
四川--Tomp (139776)
2010-09-06 15:19:29
你们先进行,我做完事情就来。我等会去查记录
*
广东-Joshua (470534800)
2010-09-06 15:19:31
active 如果是false就会隐藏了
#
上海-Jeff (85822082)
2010-09-06 15:21:49
active如果是false,就会掩藏这个记录,但是对他的子记录有什么影响呢?
#
广东-Joshua (470534800)
2010-09-06 15:22:37
子记录?
#
重庆-mrshelly (49812643)
2010-09-06 15:23:18
应该没有影响. 看OE的东西. 一般子记录就没有入口了. 所以看不到.
#
重庆-mrshelly (49812643)
2010-09-06 15:23:26
也可以从一定意义上讲是隐藏了.
#
上海-Jeff (85822082)
2010-09-06 15:23:40
比如partner被deactive了
#
重庆-mrshelly (49812643)
2010-09-06 15:23:45
这一块OE应该没有做太严谨...
#
上海-Jeff (85822082)
2010-09-06 15:24:07
它的address应该有个partner字段,会显示啥?
#
重庆-mrshelly (49812643)
2010-09-06 15:24:43
看不懂.
#
上海-Jeff (85822082)
2010-09-06 15:25:04
address是有单独的菜单入口的,shelly
#
重庆-mrshelly (49812643)
2010-09-06 15:25:36
active 你指谁的 acitve ?
#
重庆-mrshelly (49812643)
2010-09-06 15:25:40
partner?
#
上海-Jeff (85822082)
2010-09-06 15:25:45
恩
#
重庆-mrshelly (49812643)
2010-09-06 15:25:51
应该可以看得到.
#
重庆-mrshelly (49812643)
2010-09-06 15:26:04
我估计 address 的 partner 应该也看得到.
#
重庆-mrshelly (49812643)
2010-09-06 15:26:10
谁测试一下?
#
上海-Jeff (85822082)
2010-09-06 15:26:23
主数据active=false了的话,对业务数据有啥影响,这个比较关键
#
重庆-mrshelly (49812643)
2010-09-06 15:26:38
OE应该也没有处理.
#
上海-Jeff (85822082)
2010-09-06 15:29:43
测试结果、没有处理
#
四川--Tomp (139776)
2010-09-06 15:30:05
谁在记录?
#
上海-Jeff (85822082)
2010-09-06 15:30:13
qq在记录
#
重庆-mrshelly (49812643)
2010-09-06 15:30:14
看OE的代码风格, 应该就可以看得出来.
#
上海-Jeff (85822082)
2010-09-06 15:30:35
机制是这样
#
上海-Jeff (85822082)
2010-09-06 15:31:26
如果一个产品有销售订单,那么你设置产品的active=false以后
#
上海-Jeff (85822082)
2010-09-06 15:31:52
无论是输入新订单还是查看产品列表,你都看不到这个产品了
#
上海-Jeff (85822082)
2010-09-06 15:32:10
但是旧的订单上,这个产品还在
#
重庆-mrshelly (49812643)
2010-09-06 15:33:02
所以说啊.
#
上海-Jeff (85822082)
2010-09-06 15:33:09
所以这个逻辑删除和物理删除是有区别的,不需要做检查
#
重庆-mrshelly (49812643)
2010-09-06 15:33:11
OE的大部分都是在 view 端控制的.
#
重庆-mrshelly (49812643)
2010-09-06 15:33:38
也就是说, 只是把入口切断了.
#
上海-Jeff (85822082)
2010-09-06 15:33:50
如果你把这个product删除了,相信会有一个方法检查是否有历史记录,然后报错
#
重庆-mrshelly (49812643)
2010-09-06 15:33:51
这种方式是很不科学的.
#
上海-Jeff (85822082)
2010-09-06 15:34:09
我觉得这个逻辑是合理的
#
上海-Jeff (85822082)
2010-09-06 15:41:11
'active': lambda *a: 1,
#
上海-Jeff (85822082)
2010-09-06 15:50:15
partner address的字段看完了,接下来看方法
#
上海-Jeff (85822082)
2010-09-06 15:50:32
305行 def name_get
#
重庆-mrshelly (49812643)
2010-09-06 15:51:38
显示地址的方式... 没有必要看.
#
广东-Joshua (470534800)
2010-09-06 15:51:53
来了..
#
上海-Jeff (85822082)
2010-09-06 15:52:08
比较有意思,需要显示联系人就显示人名,需要显示地址就用逗号把地址串起来
#
上海-Jeff (85822082)
2010-09-06 15:53:24
感觉这里应该重写,中国的地址顺序和国际友人不同
#
重庆-mrshelly (49812643)
2010-09-06 15:53:37
嗯...
#
重庆-mrshelly (49812643)
2010-09-06 15:53:44
这个不是重点. 个人觉得.
#
上海-Jeff (85822082)
2010-09-06 15:53:50
咱从大处着眼,从中国写起,他们先写门牌号
#
广东-Joshua (470534800)
2010-09-06 15:53:56
OE里面很多name_get都喜欢这样连起来
#
广东-Joshua (470534800)
2010-09-06 15:54:03
嗯,可以跳下一个
#
上海-Jeff (85822082)
2010-09-06 15:54:27
def name_search
#
上海-Jeff (85822082)
2010-09-06 15:54:38
这是干啥用的?
#
重庆-mrshelly (49812643)
2010-09-06 15:55:10
自定义查找方法.
#
重庆-mrshelly (49812643)
2010-09-06 15:55:18
一般的检索, 就是在字段内检索.
#
重庆-mrshelly (49812643)
2010-09-06 15:55:32
自定义后, 就可以乱七八糟检索.
#
广东-Joshua (470534800)
2010-09-06 15:55:44
嗯
#
广东-Joshua (470534800)
2010-09-06 15:56:10
先搜name 再搜 zip 再乱七八糟
#
重庆-mrshelly (49812643)
2010-09-06 15:56:17
看,代码里, 可以按 zip 按 city 按 name
#
上海-Jeff (85822082)
2010-09-06 15:56:40
我试着来解释一下
#
广东-Joshua (470534800)
2010-09-06 15:56:44
这个很多地方都有
#
上海-Jeff (85822082)
2010-09-06 15:56:52
既然显示是分人名和地址
#
上海-Jeff (85822082)
2010-09-06 15:57:29
那输入的时候 用输入的文字去找adress对象 也是走两条路
#
上海-Jeff (85822082)
2010-09-06 15:58:11
比如你找联系人,那就输入 张,会搜出来姓张的人
#
上海-Jeff (85822082)
2010-09-06 15:58:49
但找地址就不能输入 张 了,而应该是如 解放碑 之类的
#
上海-Jeff (85822082)
2010-09-06 15:58:56
agree ?
#
重庆-mrshelly (49812643)
2010-09-06 15:59:02
嗯...
#
重庆-mrshelly (49812643)
2010-09-06 15:59:17
就如同 解放碑这个地址里有一个"张灰牛肉"
#
重庆-mrshelly (49812643)
2010-09-06 15:59:29
然后 你检索的时候, 会出来"解放碑"
#
上海-Jeff (85822082)
2010-09-06 15:59:53
所以说 name_get 是用来显示many2one的
#
重庆-mrshelly (49812643)
2010-09-06 15:59:55
这一块儿 在整个框架中, 做得不错... 企业里很多这样的应用.
#
上海-Jeff (85822082)
2010-09-06 16:00:15
name_search是用来帮助输入many2one的
#
广东-Joshua (470534800)
2010-09-06 16:00:24
对的!
#
上海-Jeff (85822082)
2010-09-06 16:01:04
def get_city
#
上海-Jeff (85822082)
2010-09-06 16:01:17
这是干嘛用的,哪里调用了?
#
重庆-mrshelly (49812643)
2010-09-06 16:01:59
我估计查邮编的时候可能要用到.
#
上海-Jeff (85822082)
2010-09-06 16:05:13
好的,partner address整完了,咱还回到partner
#
上海-Jeff (85822082)
2010-09-06 16:05:24
现在我们在148行
#
上海-Jeff (85822082)
2010-09-06 16:06:04
'category_id': fields.many2many('res.partner.category', 'res_partner_category_rel', 'partner_id', 'category_id', 'Categories'),
#
上海-Jeff (85822082)
2010-09-06 16:07:58
原来我不明白数据库咋存储多对多,现在理解应该是用 res_partner_category_rel 把关系拆成了两个一对多,是么?
#
重庆-mrshelly (49812643)
2010-09-06 16:08:28
就是把两个对象用一个中间表 来关联 对象之间的关系.
#
广东-Joshua (470534800)
2010-09-06 16:08:38
是的
#
上海-Jeff (85822082)
2010-09-06 16:09:25
class res_partner_category
#
上海-Jeff (85822082)
2010-09-06 16:09:51
按惯例,我们又跳到 54行 了
#
重庆-mrshelly (49812643)
2010-09-06 16:10:08
为什么要这样惯?
#
重庆-mrshelly (49812643)
2010-09-06 16:10:15
跳来跳切好麻烦.
#
重庆-mrshelly (49812643)
2010-09-06 16:10:20
顺着看就行了.
#
广东-Joshua (470534800)
2010-09-06 16:11:37
#
广东-Joshua (470534800)
2010-09-06 16:11:49
看错了
#
上海-Jeff (85822082)
2010-09-06 16:12:04
我倒觉得这个需求和我们做的那个 显示会计科目全名很像
#
重庆-mrshelly (49812643)
2010-09-06 16:12:16
是的.
#
广东-Joshua (470534800)
2010-09-06 16:12:21
name_get?
#
上海-Jeff (85822082)
2010-09-06 16:12:50
恩
#
上海-Jeff (85822082)
2010-09-06 16:13:14
def name_get
#
广东-Joshua (470534800)
2010-09-06 16:13:22
是啊,我覆盖了旧的方法
#
上海-Jeff (85822082)
2010-09-06 16:13:34
reads = self.read(cr, uid, ids, ['name','parent_id'], context=context)
#
上海-Jeff (85822082)
2010-09-06 16:14:26
请问两位老师,这样会把 father拿出来,还是连father 的 father的father都能一起俘虏?
#
上海-Jeff (85822082)
2010-09-06 16:16:50
果然是把整个 一大家子都拿出来了
#
广东-Joshua (470534800)
2010-09-06 16:16:57
reads = self.read(cr, uid, ids, ['name','parent_id'], context=context)
#
上海-Jeff (85822082)
2010-09-06 16:17:20
高手给讲讲,怎么做到的? 不用循环就能取到?
#
广东-Joshua (470534800)
2010-09-06 16:17:26
for record in reads:
name = record['name']
if record['parent_id']:
name = record['parent_id'][1]+' / '+name
res.append((record['id'], name))
#
重庆-mrshelly (49812643)
2010-09-06 16:17:38
关键我估计要看 context 里的东西吧?
#
广东-Joshua (470534800)
2010-09-06 16:18:08
for循环吧
#
上海-Jeff (85822082)
2010-09-06 16:18:22
不是,我懂了
#
上海-Jeff (85822082)
2010-09-06 16:18:36
这样只会把 father拿出来
#
上海-Jeff (85822082)
2010-09-06 16:18:58
grandfather抓不到
#
广东-Joshua (470534800)
2010-09-06 16:19:03
看来只是只是找出直系
#
重庆-mrshelly (49812643)
2010-09-06 16:19:03
然后 father 取 name 的时候, 应该又会调用 name_get
#
上海-Jeff (85822082)
2010-09-06 16:19:09
是的
#
广东-Joshua (470534800)
2010-09-06 16:19:12
哈对!
#
上海-Jeff (85822082)
2010-09-06 16:19:32
所以这里的for循环只会执行一次,根本就是fetchone
#
重庆-mrshelly (49812643)
2010-09-06 16:19:35
这就是 osv 中的牛X地方.
#
广东-Joshua (470534800)
2010-09-06 16:19:50
这段可以改善我之前写的那个
#
重庆-mrshelly (49812643)
2010-09-06 16:20:02
嗯...
#
上海-Jeff (85822082)
2010-09-06 16:22:54
def _check_recursion
#
重庆-mrshelly (49812643)
2010-09-06 16:23:06
_check_recursion
讨论过了.
#
上海-Jeff (85822082)
2010-09-06 16:24:43
level = 100
while len(ids):
cr.execute('select distinct parent_id from res_partner_category where id in %s', (tuple(ids),))
#
上海-Jeff (85822082)
2010-09-06 16:24:55
这几句看懂了
#
上海-Jeff (85822082)
2010-09-06 16:25:12
ids = filter(None, map(lambda x:x[0], cr.fetchall()))
这是啥啊?
#
重庆-mrshelly (49812643)
2010-09-06 16:25:37
这句, 就是取出 给定的 ids 里所有 父 ids
#
广东-Joshua (470534800)
2010-09-06 16:25:40
这句靠水哥
#
广东-Joshua (470534800)
2010-09-06 16:27:03
filter()这个是python的方法??
#
重庆-mrshelly (49812643)
2010-09-06 16:28:15
这一句, 那个 filter 应该是多余的.
#
上海-Jeff (85822082)
2010-09-06 16:28:58
是不是把空值干掉啊
#
重庆-mrshelly (49812643)
2010-09-06 16:29:20
不是...
#
上海-Jeff (85822082)
2010-09-06 16:29:30
cr.fetchall() 返回的可能会有一些空的element
#
重庆-mrshelly (49812643)
2010-09-06 16:29:35
这句的作用是, 用 None 去过滤 map 的结果.
#
重庆-mrshelly (49812643)
2010-09-06 16:29:49
因为方法是 None 也就是不过滤.
#
重庆-mrshelly (49812643)
2010-09-06 16:30:07
所以, 理论上, 这句与 map(lambda x:x[0], cr.fetchall())
#
重庆-mrshelly (49812643)
2010-09-06 16:30:11
是一样的效果.
#
上海-Jeff (85822082)
2010-09-06 16:31:12
超出会计的理解能力了
#
重庆-mrshelly (49812643)
2010-09-06 16:31:44
所以, 就理解为 取出 给定类别的所有父类ID
#
重庆-mrshelly (49812643)
2010-09-06 16:31:46
就行了.
#
重庆-mrshelly (49812643)
2010-09-06 16:31:53
读代码, 应该 先块级读.
#
重庆-mrshelly (49812643)
2010-09-06 16:31:57
再去行读.
#
上海-Jeff (85822082)
2010-09-06 16:32:44
if not level:
return False
level -= 1
#
重庆-mrshelly (49812643)
2010-09-06 16:33:00
这个是递归只允许 100级.
#
上海-Jeff (85822082)
2010-09-06 16:33:08
这个是避免死循环的吧,读到100级就放弃了
#
重庆-mrshelly (49812643)
2010-09-06 16:33:27
也就是说, 如果你的 partner 分类 级达到100级以上,,, 呵呵... OE就不会管这个限制了.
#
重庆-mrshelly (49812643)
2010-09-06 16:33:31
估计米人这么变态.
#
上海-Jeff (85822082)
2010-09-06 16:33:47
_description='Partner Categories'
_name = 'res.partner.category'
_columns = {
'name': fields.char('Category Name', required=True, size=64, translate=True),
'parent_id': fields.many2one('res.partner.category', 'Parent Category', select=True),
'complete_name': fields.function(_name_get_fnc, method=True, type="char", string='Full Name'),
'child_ids': fields.one2many('res.partner.category', 'parent_id', 'Child Categories'),
'active' : fields.boolean('Active', help="The active field allows you to hide the category without removing it."),
}
#
上海-Jeff (85822082)
2010-09-06 16:34:31
这些应该没什么需要解释的吧,如果大家对function字段类型没意见的话
#
广东-Joshua (470534800)
2010-09-06 16:35:12
嗯
#
上海-Jeff (85822082)
2010-09-06 16:35:14
_constraints = [
(_check_recursion, 'Error ! You can not create recursive categories.', ['parent_id'])
]
_defaults = {
'active' : lambda *a: 1,
}
_order = 'parent_id,name'
#
上海-Jeff (85822082)
2010-09-06 16:35:59
第一次碰到_constraints,这个是server级别的,还有个数据库级别的
#
上海-Jeff (85822082)
2010-09-06 16:36:14
那我们回去partner ?
#
广东-Joshua (470534800)
2010-09-06 16:36:28
嗯
#
上海-Jeff (85822082)
2010-09-06 16:36:58
'events': fields.one2many('res.partner.event', 'partner_id', 'Events'),
#
上海-Jeff (85822082)
2010-09-06 16:37:26
为了避免跳出去找不到回来的路,我们这次就不跳了吧
#
重庆-mrshelly (49812643)
2010-09-06 16:37:46
server 级的.
#
重庆-mrshelly (49812643)
2010-09-06 16:37:53
这一块就是不太清楚.
#
上海-Jeff (85822082)
2010-09-06 16:37:55
这里可以算个crm的雏形,用不起crm的穷公司可以先用这个记录一下
#
重庆-mrshelly (49812643)
2010-09-06 16:38:19
这个 _check_recursion 为 True 时候, 是允许, 还是 False 的时候 允许呢?
#
上海-Jeff (85822082)
2010-09-06 16:40:40
shelly好问题!
#
广东-Joshua (470534800)
2010-09-06 16:43:22
False--> Error ! You can not create recursive categories.
#
上海-Jeff (85822082)
2010-09-06 16:44:58
应该是false的时候报错,true的时候直接通过
#
重庆-mrshelly (49812643)
2010-09-06 16:45:07
嗯.. 想想 _check_recursion 在什么情况下, 会 return False?
#
广东-Joshua (470534800)
2010-09-06 16:45:25
真的搞100层
#
重庆-mrshelly (49812643)
2010-09-06 16:45:25
not level
#
重庆-mrshelly (49812643)
2010-09-06 16:45:33
不对劲啊.
#
重庆-mrshelly (49812643)
2010-09-06 16:45:40
哦.
#
重庆-mrshelly (49812643)
2010-09-06 16:45:41
对的.
#
上海-Jeff (85822082)
2010-09-06 16:45:44
shelly我觉得你你对那句话的解释不对啊
#
重庆-mrshelly (49812643)
2010-09-06 16:45:58
如果是死 循环的话, 就很容易超过 100
#
重庆-mrshelly (49812643)
2010-09-06 16:46:05
然后 就 return False 了.
#
广东-Joshua (470534800)
2010-09-06 16:46:15
哦~
上海-Jeff (85822082)
2010-09-06 16:47:24
然后42再找35,35再找42
#
重庆-mrshelly (49812643)
2010-09-06 16:47:39
level 一直减
#
重庆-mrshelly (49812643)
2010-09-06 16:47:45
减到 0 就返回.
#
广东-Joshua (470534800)
2010-09-06 16:47:48
经测试是这样的!
#
重庆-mrshelly (49812643)
2010-09-06 16:47:59
呵呵... OE的这个算法有些意思.
#
上海-Jeff (85822082)
2010-09-06 16:48:24
值得一读
#
上海-Jeff (85822082)
2010-09-06 16:48:50
有些for循环只跑一次,有些while循环总跑100次
#
重庆-mrshelly (49812643)
2010-09-06 16:49:15
但是 如果我写.
#
重庆-mrshelly (49812643)
2010-09-06 16:49:24
我肯定把查到的 id push 到一个地方.
#
重庆-mrshelly (49812643)
2010-09-06 16:49:28
然后 继续查...
#
重庆-mrshelly (49812643)
2010-09-06 16:49:42
只要新查到的 parent 已经push 过了.
#
上海-Jeff (85822082)
2010-09-06 16:49:43
有重复的就结束了
#
重庆-mrshelly (49812643)
2010-09-06 16:49:47
就直接 return False 了.
#
重庆-mrshelly (49812643)
2010-09-06 16:49:58
他这个算法有问题.
#
重庆-mrshelly (49812643)
2010-09-06 16:50:00
有些耗.
#
重庆-mrshelly (49812643)
2010-09-06 16:50:14
即使是100级.
#
重庆-mrshelly (49812643)
2010-09-06 16:50:21
也才保存 100 个id 而已.
#
重庆-mrshelly (49812643)
2010-09-06 16:50:26
内存耗用也不会太大.
#
重庆-mrshelly (49812643)
2010-09-06 16:51:13
35 -->[35]
35 查到 42
42 -->[35, 42]
42 查到 48
#
重庆-mrshelly (49812643)
2010-09-06 16:51:21
48 --> [35,42,48]
#
重庆-mrshelly (49812643)
2010-09-06 16:51:25
48查到 38
#
重庆-mrshelly (49812643)
2010-09-06 16:51:36
48查到35
#
重庆-mrshelly (49812643)
2010-09-06 16:51:39
然后 就判断出来了.
#
广东-Joshua (470534800)
2010-09-06 16:51:40
有改进的点了
#
重庆-mrshelly (49812643)
2010-09-06 16:51:51
只有几次循环, 不用他那样循环100次.
#
上海-Jeff (85822082)
2010-09-06 16:51:57
他现在8行代码,你那么写16行不止
#
重庆-mrshelly (49812643)
2010-09-06 16:52:07
不公.
#
重庆-mrshelly (49812643)
2010-09-06 16:52:09
不会.
#
重庆-mrshelly (49812643)
2010-09-06 16:52:13
即使16行.
#
重庆-mrshelly (49812643)
2010-09-06 16:52:18
我也会选择16行的代码.
#
重庆-mrshelly (49812643)
2010-09-06 16:52:39
16 行的好处就是 少了很多次循环.
#
重庆-mrshelly (49812643)
2010-09-06 16:52:51
而且它这里, 还要查询数据库.
#
重庆-mrshelly (49812643)
2010-09-06 16:52:56
100次查询.
#
重庆-mrshelly (49812643)
2010-09-06 16:53:01
这个优化还是很值得的.
#
上海-Jeff (85822082)
2010-09-06 16:53:02
这个在深入就是人生观了,到此为止
#
广东-Joshua (470534800)
2010-09-06 16:53:10
#
上海-Jeff (85822082)
2010-09-06 16:53:35
'credit_limit': fields.float(string='Credit Limit'),
#
上海-Jeff (85822082)
2010-09-06 16:54:04
这个应该在account或者sale才用到的字段,这么早就放进去啦
#
上海-Jeff (85822082)
2010-09-06 16:54:18
'ean13': fields.char('EAN13', size=13),
#
上海-Jeff (85822082)
2010-09-06 16:54:31
partner也支持条码输入
#
重庆-mrshelly (49812643)
2010-09-06 16:54:35
嗯...
#
上海-Jeff (85822082)
2010-09-06 16:54:42
'active': fields.boolean('Active'),
#
重庆-mrshelly (49812643)
2010-09-06 16:54:48
我很最以前, 就玩过条码管理供应端.
#
重庆-mrshelly (49812643)
2010-09-06 16:54:57
不过是做的是供应商的车辆管理.
#
上海-Jeff (85822082)
2010-09-06 16:55:10
'customer': fields.boolean('Customer', help="Check this box if the partner is a customer."),
'supplier': fields.boolean('Supplier', help="Check this box if the partner is a supplier. If it's not checked, purchase people will not see it when encoding a purchase order."),
#
重庆-mrshelly (49812643)
2010-09-06 16:55:22
每个供应商需要有个条码... 才能门禁确认.
#
上海-Jeff (85822082)
2010-09-06 16:55:33
'city':fields.related('address','city',type='char', string='City'),
#
重庆-mrshelly (49812643)
2010-09-06 16:55:52
related ?
#
上海-Jeff (85822082)
2010-09-06 16:56:01
related是啥意思来着,偶查查
#
重庆-mrshelly (49812643)
2010-09-06 16:56:07
相关...
#
粤-Black Jack (104144648)
2010-09-06 16:56:47
引用的号码就是引用的记录
#
重庆-mrshelly (49812643)
2010-09-06 16:57:49
号码?
#
上海-Jeff (85822082)
2010-09-06 16:58:22
这个牛啊
#
粤-Black Jack (104144648)
2010-09-06 16:58:48
。。。sorry就理解为对象引用的一个标记
#
四川--Tomp (139776)
2010-09-06 16:58:56
我忙完了。
#
粤-Black Jack (104144648)
2010-09-06 16:59:06
那例子就是引用city
#
粤-Black Jack (104144648)
2010-09-06 16:59:32
不过type是什么意思
#
上海-Jeff (85822082)
2010-09-06 17:02:25
related:
Sometimes you need to refer to the relation of a relation. For example, supposing you have objects: City -> State -> Country, and you need to refer to the Country from a City, you can define a field as below in the City object:
'country_id': fields.related(
'state_id',
'country_id',
type="many2one",
relation="module.country",
string="Country",
store=False)
#
上海-Jeff (85822082)
2010-09-06 17:03:38
我英文不好,哪位能翻译
#
粤-Black Jack (104144648)
2010-09-06 17:04:32
。。。
#
广东-Joshua (470534800)
2010-09-06 17:04:51
有时你需要参考的一个关系的关系。举例来说,假如你有对象:城市 - >状态 - >国家,你需要把一个城市到乡村,你可以定义为城市中的对象以下字段:
#
上海-Outlook (8492321)
2010-09-06 17:05:31
这里refer表示引用比较恰当
#
粤-Black Jack (104144648)
2010-09-06 17:05:50
就是说在确定city时那个count就可以用relation
#
上海-Jeff (85822082)
2010-09-06 17:06:07
把一个城市到乡村,哥,你笑死我了
#
粤-Black Jack (104144648)
2010-09-06 17:06:19
系统给出一个选择图标出来
#
重庆-mrshelly (49812643)
2010-09-06 17:06:36
好象可以理解为 如果partner 有设置 address City 的话, 就直接沿用 这个 City
#
粤-Black Jack (104144648)
2010-09-06 17:07:03
这怎么理解?
#
重庆-mrshelly (49812643)
2010-09-06 17:07:31
不过, 好象 partner 先建 address 后建吧. 通常情况.
#
浙江-疯子 (15369940)
2010-09-06 17:07:49
没称市就用弟子
#
粤-Black Jack (104144648)
2010-09-06 17:07:52
有设置的
#
重庆-mrshelly (49812643)
2010-09-06 17:08:28
哦. 取 address 这个字段对象的 city 值做为本记录的值.
#
上海-Jeff (85822082)
2010-09-06 17:10:08
重庆-mrshelly(49812643) 5:08:28 PM
哦. 取 address 这个字段对象的 city 值做为本记录的值.niubility !
#
上海-Jeff (85822082)
2010-09-06 17:30:20
下一个问题是多个adress的city不一样看它用哪一个
#
上海-Jeff (85822082)
2010-09-06 17:31:17
放学了,谢谢各位!
#
重庆-mrshelly (49812643)
2010-09-06 17:31:20
嗯.. 正要问到这里, 多个 address 它如何选择.