我使用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'))
答案 0 :(得分:8)
可悲的是,OpenERP / Odoo仅支持在变更方法,计算方法和记录修改跟踪中的一个级别的关系。
开箱即用,允许使用父/子设置(如在FORMVIEW.one2manyLIST中),但不允许使用祖父/父/子(如在FORMVIEW.one2manyLIST.one2manyLIST中)。
这方面的一个例子是:
对模型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