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

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

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

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

今天开始OpenERP进销存源码分析活动



  • HornERP3.0的目标是针对OpenERP的核心进销存模块做增强和简化。那么前提就是对系统原有的功能和逻辑有深入了解,为了满足这个前提,HornERP项目组决定从今天(2010-9-1)开始,对OpenERP5.0.12版本的stock、purchase、sale、mrp等进销存涉及的基础模块做源码级的研究和分析。为保持开源项目的开放性,此活动以下面方式运作:

    1、每天此帖公布当天分析的对象和次日分析的计划
    2、分析和讨论在qq群中进行,聊天记录整理后记录到此帖
    3、根据分析结果完善中文帮助的wiki
    4、根据分析结果完善各模块对应的翻译
    5、根据分析结果总结出HornERP3.0对应的需求

    愿意参与的,有更好的运作建议的,已经有心得的,马上加入我们!!



  • HornERP3.0的目标是针对OpenERP的核心进销存模块做增强和简化。那么前提就是对系统原有的功能和逻辑有深入了解,为了满足这个前提,HornERP项目组决定从今天(2010-9-1)开始,对OpenERP5.0.12版本的stock、purchase、sale、mrp等进销存涉及的基础模块做源码级的研究和分析。为保持开源项目的开放性,此活动以下面方式运作:

    1、每天此帖公布当天分析的对象和次日分析的计划
    2、分析和讨论在qq群中进行,聊天记录整理后记录到此帖
    3、根据分析结果完善中文帮助的wiki
    4、根据分析结果完善各模块对应的翻译
    5、根据分析结果总结出HornERP3.0对应的需求

    愿意参与的,有更好的运作建议的,已经有心得的,马上加入我们!!


  • 管理员

    欢迎喜欢源码分析的筒子踊跃参与 :P



  • 报个名先!



  • 研究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,
        } 
    有缺省值



  • 俺喜欢的mm



  •       上次说到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 它如何选择.


  • 管理员

    关于partner的源码分析整理到WIKI里面 :)
    http://wiki.shine-it.net/index.php?title=codereview_base



  • well done, I modified it to give you some color see see


登录后回复
 

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