2014-09-30 209 views
4

我已经使用One2many字段扩展了'account.analytic.account'模型,该字段使用One2many字段引用了第二个模型。如何浏览或搜索Odoo中的One2many字段?

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

例如:

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')) 

回答

6

可悲的OpenERP/Odoo只支持在变化的方法,计算方法,并记录修改跟踪关系的一个水平。

因此,开箱即可允许父/子设置(如在FORMVIEW.one2manyLIST中),但不允许祖父母/父母/子女(如在FORMVIEW.one2manyLIST.one2manyLIST中)。

这方面的一个例子是:

  • 型号A - 提交作为服务保证形式(祖父母模型)
  • 模型B - 覆盖的位置的列表(父模型,与REF祖父母)
  • 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