如何在Odoo中浏览或搜索One2many字段?

时间:2014-09-30 15:33:11

标签: python openerp odoo

我使用One2many字段扩展了'account.analytic.account'模型,该字段引用了具有One2many字段的第二个模型。

当我尝试从compute方法迭代第二个One2many字段时,它只列出刚刚添加的记录。在保存父记录之前,先前的记录(在界面上可见)在使用“自我”上下文的代码中不可见。

示例:

for line in self.One2manyField:
    #only gets here for records I add during current session, or all records if parent is saved
    #how can I see previously saved records? 

以下是代码:

1。)扩展'account.analytic.account'模型

class account_analytic_account(models.Model):

    _inherit = ['account.analytic.account']

    service_location_ids = fields.One2many(comodel_name='contract.service.location', inverse_name='contract_id', copy=True)

2.)首先引用One2many模型:

class Contract_Service_Location(models.Model):
    _name = 'contract.service.location'
    _description = 'Service Location Record'  

    #problem is here!
    #compute method for subtotal field
    @api.one    
    @api.depends('recurring_line_ids','recurring_line_ids.price_subtotal')
    def _compute_subtotal(self):
        total = 0.0

        #I tried to get previously saved ids, but returns nothing, until parent record is saved
        old_ids = self.env['contract.recurring.line'].search([('service_location_id', '=', self.id)]) 

        #this only works for new entries during same session, or until parent record is saved. Why?
        for line in self.recurring_line_ids:
            total = total + line.price_subtotal

        #set field
        self.price_subtotal = total

    contract_id = fields.Many2one(comodel_name='account.analytic.account')
    fiscal_position = fields.Many2one(comodel_name='account.fiscal.position', string='Default Taxes')
    partner_id = fields.Many2one(comodel_name='res.partner', string='Service Location', help='Optional seperate billing address from customer AND service locations',required=True)
    sequence = fields.Integer(string='Sequence', help="Gives the sequence order when displaying a list of sales order lines.")
    price_subtotal = fields.Float(compute='_compute_subtotal', string='Subtotal', digits_compute= dp.get_precision('Account'), readonly=True, store=True)
    pricelist_id = fields.Many2one(comodel_name='product.pricelist', string='Pricelist', required=True, help="Pricelist for current customer.", default=_get_default_pricelist)
    recurring_line_ids = fields.One2many(comodel_name='contract.recurring.line', inverse_name='service_location_id', copy=True)

3.)第二个引用的One2many模型:

class Contract_Recurring_Line(models.Model):
    _name = 'contract.recurring.line'
    _description = 'Recurring Service Location Line'


    @api.one
    @api.depends('price_unit', 'discount', 'product_uom_qty','product_uos_qty',
        'product_id', 'service_location_id.partner_id','service_location_id.pricelist_id')
    def _compute_subtotal(self):
        price = self.price_unit * (1 - (self.discount or 0.0) / 100.0)
        taxes = self.tax_id.compute_all(price, self.product_uom_qty, product=self.product_id, partner=self.service_location_id.partner_id)
        self.price_subtotal = taxes['total']
        if self.service_location_id:
            self.price_subtotal = self.service_location_id.pricelist_id.currency_id.round(self.price_subtotal)


    service_location_id = fields.Many2one(comodel_name='contract.service.location', required=True, ondelete='cascade', select=True)
    name = fields.Text('Description', required=True)
    product_id = fields.Many2one('product.product', 'Product', domain=[('sale_ok', '=', True)], change_default=True, ondelete='restrict')
    price_unit = fields.Float('Unit Price', required=True, digits_compute= dp.get_precision('Product Price'))
    price_subtotal = fields.Float(compute='_compute_subtotal', string='Subtotal',store=True, readonly=True, digits_compute= dp.get_precision('Account'))
    product_uom_qty = fields.Float('Quantity', default=float(1), digits_compute= dp.get_precision('Product UoS'))
    discount = fields.Float('Discount (%)', digits_compute= dp.get_precision('Discount'))

1 个答案:

答案 0 :(得分:8)

可悲的是,OpenERP / Odoo仅支持在变更方法,计算方法和记录修改跟踪中的一个级别的关系。

开箱即用,允许使用父/子设置(如在FORMVIEW.one2manyLIST中),但不允许使用祖父/父/子(如在FORMVIEW.one2manyLIST.one2manyLIST中)。

这方面的一个例子是:

  • A型 - 作为服务保证表格(祖父母模型)
  • 模型B - 覆盖位置列表(父模型,带祖父母的参考)
  • 模型C - 为每个覆盖的位置提供的服务列表(子模型,引用父级)

对模型A所做的更改不会保存模型C的记录,而模型A上的变更/计算方法不能使用模型C上的字段

因此,如果对第二个嵌套的one2many字段进行了更改,或者即使您尝试读取嵌套的one2many字段,更改也会丢失并出现上述问题。

我创建了一个解决方案,为onchange / compute / write添加了另一个级别的跟踪。您需要修改Odoo的核心。我希望将来会发生这种变化,因为Odoo SA已将其列为github上的“愿望清单”。

以下是Odoo 8.0的代码。 YMMV与其他版本。

FIELDS.PY/_RelationalMulti Class。替换以下方法:

def convert_to_write(self, value, target=None, fnames=None):
    # remove/delete former records
    if target is None:
        set_ids = []
        result = [(6, 0, set_ids)]
        add_existing = lambda id: set_ids.append(id)
    else:
        tag = 2 if self.type == 'one2many' else 3
        result = [(tag, record.id) for record in target[self.name] - value]
        add_existing = lambda id: result.append((4, id))

    if fnames is None:
        # take all fields in cache, except the inverses of self
        fnames = set(value._fields) - set(MAGIC_COLUMNS)
        for invf in self.inverse_fields:
            fnames.discard(invf.name)

    # add new and existing records
    for record in value:
        if not record.id or record._dirty:
            values = dict((k, v) for k, v in record._cache.iteritems() if k in fnames)
            tempVal = {}
            for n in values:
                f = record._fields[n] #get field def
                if f.type == 'one2many':
                    subrec = record[n]
                    subfields = subrec._fields                    
                    tempVal[n] = f.convert_to_write(subrec,record)
                else:
                    val = {}
                    val[n] = values.get(n)
                    tempVal[n] = record._convert_to_write(val)[n]

            if tempVal:
                values = tempVal       

            #add to result       
            if not record.id:
                    result.append((0, 0, values))
            else:
                result.append((1, record.id, values))
        else:
            add_existing(record.id)

    return result

在MODELS.py/BaseModel类中,替换以下方法:

@api.model
def new(self, values={}):
    """ new([values]) -> record

    Return a new record instance attached to the current environment and
    initialized with the provided ``value``. The record is *not* created
    in database, it only exists in memory.
    """
    record = self.browse([NewId()])
    record._cache.update(record._convert_to_cache(values, update=True))

    if record.env.in_onchange:
        # The cache update does not set inverse fields, so do it manually.
        # This is useful for computing a function field on secondary
        # records, if that field depends on the main record.
        for name in values:
            field = self._fields.get(name)
            if field:
                try:
                    for invf in field.inverse_fields:
                        invf._update(record[name], record)

                        #serarch this field for sub inverse fields
                        for ftmp in self[name]._fields: 

                                f = self[name]._fields.get(ftmp) 
                                if f and f != invf:                  
                                    for invf in f.inverse_fields:
                                        val = record[name]
                                        invf._update(record[name][ftmp], val)

                except:
                    pass

    return record