Динамический домен для поля Many2one

Коллеги, подскажите, как навесить вычисляемый домен в таком случае:

В Odoo 8 есть сущность «Договор», в которой
1. Указан «Поставщик» — ссылка на сущность «Партнер»
2. Есть поле «Ответственный со стороны поставщика» — ссылка на сущность «Контакт»

«Партнер» и «Контакт» связаны Many2Many

Нужно, чтобы в поле «Ответственный со стороны поставщика», в лукапе показывались только те контакты, которые связаны с указанным поставщиком.

Пробовал писать в модели сущности «Договор»:

	@api.onchange('vendor_id')
	def onchange_vendor(self):
		res = {}
		if self.vendor_id:
			vendor_contact_ids = self.vendor_id.contact_ids.mapped('id')
			res['domain'] = {'vendcontact_id': [('id', 'in', vendor_contact_ids)]}
		else:
			res['domain'] = {'vendcontact_id': [('id', 'in', [])]

		return res

Этот метод срабатывает только при создании нового договора или при изменении поля «Поставщик». Как быть в случае, когда форма уже существующего договора открывается на редактирование, но пользователь не трогает поле «Поставщик»?

9 комментариев

avatar
когда-то я кодировал…
может быть есть событие на открытие окна на редактирование? там и вызови этот код…
avatar
Пока не нашел такого события :(

Есть fields_view_get(), но внутри него мне так и не удалось добраться до значений полей текущей записи:

	def fields_view_get(self, cr, user, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
		if context is None:
			context = {}
		res = super(my.agreement, self).fields_view_get(cr, user, view_id, view_type, context, toolbar=toolbar, submenu=submenu)
		if view_type == 'form':
			active_id = self.env.context.get('active_id', False) # <-- Падает тут, говорит, что нет env
			vendor = self.env['my.agreement'].browse(active_id).vendor_id
			vendor_contact_ids = self.vendor_id.contact_ids.mapped('id')

			for field in res['fields']:
				if field == 'vendcontact_id':
					res['fields'][field]['domain'] = [('id', 'in', vendor_contact_ids)]
		return res
Откуда же тут будет env, если у Вас функция в старом стиле написана.
Попробуйте переписать в старом стиле:

...
active_id = context.get('active_id', False) # <-- Падает тут, говорит, что нет env
vendor = self.pool.get('my.agreement').browse(active_id).vendor_id
avatar
Вы правы. Но это меня не приближает к победе — active_id в момент срабатывания fields_view_get() имеет значение False.

Вторая ветка моих исследований — это указание в xml формы в качестве домена названия какого-нибудь вычисляемого поля:

    <field name="vendcontact_id" domain="[('id', 'in', [get_vendcontact_ids])]"/>

Но тут появляется проблема с типом этого поля. В случае, если это one2Many и он заполнен какими-то id контактов, то при нажатии кнопки лукапа появляется ошибка: TypeError: not all arguments converted during string formatting.

Работающий вариант получился только в комбинации: поле типа Char и в нем только один id контакта — тогда домен накладывается успешно.

Если в поле Char пытаться указать несколько id, например «4, 3», то появляется ошибка:

DataError: ОШИБКА: неверное значение для целого числа: «4, 3»
LINE 1: ...t".«active» = true) AND («my_contact».«id» in ('4, 3'))) ...
avatar
Нужно в fields_view_get для модели где нужен custom lookup добавлять, что-то вроде node.set('context', "{'looluppartner_id':partner_id.id,}")
X
А в самой модели res.partner — переопределить метод search. Который в случаи наличия в контексте ('looluppartner_id')
будет фильтровать вывод search.
avatar
Я правильно понимаю, что в контексте нужно передать идентификатор? Если так, то я не могу найти способ добраться до значений полей текущей записи при открытии формы.
avatar
dla starogo api — kak ta tak mozet bit.
def fields_view_get(self, cr, uid, view_id=None, view_type=False, context=None, toolbar=False, submenu=False):

if context is None: context = {}

res = super(sale_order, self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=submenu)
doc = etree.XML(res['arch'])

nodes = doc.xpath("//field[@name='order_line']/form//field[@name='product_id']")
for node in nodes:
node.set('context', "{'partner_id':parent.partner_id,}")

res['arch'] = etree.tostring(doc)
return res
avatar
Удалось найти решение тут: www.odoo.com/forum/help-1/question/complex-many2many-domains-in-views-41777

Если вкратце, то когда я пытался в домен [('id','in', get_vendcontact_ids)] подставить значение поля типа Many2many, то для системы это выглядело вот таким образом: [('id','in', [(6,0,[ID1,ID2,ID3])])], что приводило к ошибке. Чтобы корректно сформировать домен в этом случае нужно писать так: [('id','in', get_vendcontact_ids and get_vendcontact_ids[0][2])]

Для моего случая понадобилось вычисляемое поле Many2many, куда будут записываться нужные id:

get_vendcontact_ids = fields.Many2many('my.contact', compute='_get_vendcontact_ids')

@api.one
def _get_vendcontact_ids(self):
    if self.vendor_id:
        self.get_vendcontact_ids = self.vendor_id.contact_ids.mapped('id')
    else:
	self.get_vendcontact_ids = None


Его невидимым нужно вытащить в xml формы:

<field name="get_vendcontact_ids" attrs="{'invisible':True}"/>


И вот так должно выглядеть определение поля, где можно выбрать контакт только из отфильтрованного списка:

vendcontact_id = fields.Many2one('my.contact', 'Контакт поставщика', domain="[('id','in', get_vendcontact_ids and get_vendcontact_ids[0][2])]")


Уже работает. Для полноты картины еще понадобится на OnChange поля «Поставщик» привесить обновление нашего вычисляемого поля и корректное изменение значения связанного лукапа:

@api.onchange('vendor_id')
def onchange_vendor(self):
    if self.vendor_id:
        vendor_contact_ids = self.vendor_id.contact_ids.mapped('id')
        self.get_vendcontact_ids = self.vendor_id.contact_ids.mapped('id') # Обновление списка id для домена

	# Очистить поле с контактом, если он не принадлежит выбранному поставщику
	if self.vendcontact_id and self.vendcontact_id not in vendor_contact_ids:
	    self.vendcontact_id = None
    else:
        self.get_vendcontact_ids = None
	self.vendcontact_id = None


that's all
avatar
весь патч в студию )

Оставить комментарий