feat: respect set_query filters in Account selection dialog

Extract link filters from the source control's set_query/df.filters
and pass them to the backend. This ensures that field-specific
constraints (e.g. root_type=Expense, company filters) are respected
when selecting accounts through the custom dialog.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ali 2026-03-29 17:10:37 +04:00
parent 3a3b850733
commit 3dc04ed092
2 changed files with 69 additions and 1 deletions

View File

@ -634,7 +634,7 @@ def add_items_to_document(doctype, docname, items):
# ────────────────────────────────────────────────────────────────────────────
@frappe.whitelist()
def get_accounts_for_selection(filters=None, start=0, page_length=50, custom_filters=None):
def get_accounts_for_selection(filters=None, start=0, page_length=50, custom_filters=None, link_filters=None):
"""
Получить список счетов для формы подбора
@ -643,6 +643,7 @@ def get_accounts_for_selection(filters=None, start=0, page_length=50, custom_fil
start (int): Смещение для пагинации
page_length (int): Количество записей на странице
custom_filters (list): Произвольные фильтры [{field, operator, value}]
link_filters (dict): Фильтры из set_query контрола-источника
Returns:
dict: {items: [...], total_count: int}
@ -651,6 +652,7 @@ def get_accounts_for_selection(filters=None, start=0, page_length=50, custom_fil
filters = frappe.parse_json(filters) if isinstance(filters, str) else (filters or {})
custom_filters = frappe.parse_json(custom_filters) if isinstance(custom_filters, str) else (custom_filters or [])
link_filters = frappe.parse_json(link_filters) if isinstance(link_filters, str) else (link_filters or {})
start = cint(start)
page_length = cint(page_length)
@ -673,6 +675,10 @@ def get_accounts_for_selection(filters=None, start=0, page_length=50, custom_fil
.where(Account.is_group == 0)
)
# Фильтры из set_query контрола (root_type, account_type, company и т.д.)
if link_filters:
query = _apply_link_filters(query, Account, link_filters)
# Поиск по номеру и названию
search_term = cstr(filters.get('search_term', '')).strip()
if search_term:
@ -702,6 +708,8 @@ def get_accounts_for_selection(filters=None, start=0, page_length=50, custom_fil
.where(Account.disabled == 0)
.where(Account.is_group == 0)
)
if link_filters:
count_query = _apply_link_filters(count_query, Account, link_filters)
if search_term:
term = f"%{search_term}%"
count_query = count_query.where(
@ -744,6 +752,52 @@ def get_accounts_for_selection(filters=None, start=0, page_length=50, custom_fil
}
def _apply_link_filters(query, Account, link_filters):
"""
Применить фильтры из set_query контрола-источника.
Формат Frappe link_filters:
{field: value} равенство
{field: ['in', [v1, v2]]} оператор
{field: ['like', '%text%']} like
"""
account_meta = frappe.get_meta('Account')
valid_fields = {f.fieldname for f in account_meta.fields}
valid_fields.update({'name', 'owner', 'creation', 'modified'})
for field, value in link_filters.items():
if field not in valid_fields:
continue
col = Account[field]
if isinstance(value, (list, tuple)) and len(value) == 2:
op, val = value[0].lower(), value[1]
if op == 'in' and isinstance(val, (list, tuple)):
query = query.where(col.isin(val))
elif op == 'not in' and isinstance(val, (list, tuple)):
query = query.where(col.notin(val))
elif op == 'like':
query = query.where(col.like(val))
elif op == 'not like':
query = query.where(~col.like(val))
elif op == '!=':
query = query.where(col != val)
elif op == '>':
query = query.where(col > val)
elif op == '<':
query = query.where(col < val)
elif op == '>=':
query = query.where(col >= val)
elif op == '<=':
query = query.where(col <= val)
else:
query = query.where(col == val)
else:
query = query.where(col == value)
return query
def _apply_account_custom_filters(query, Account, custom_filters):
"""
Применить произвольные фильтры по полям DocType Account

View File

@ -15,10 +15,23 @@ class AccountSelectionDialog {
this.custom_filters = [];
this._account_fields = [];
this.accounts_data = [];
this.link_filters = this._extract_link_filters();
this.make_dialog();
}
// Извлечь фильтры из set_query / df.filters контрола-источника
_extract_link_filters() {
const target = this.link_selector_opts.target;
if (!target) return {};
const args = {};
if (target.set_custom_query) {
try { target.set_custom_query(args); } catch (e) { /* ignore */ }
}
return args.filters || {};
}
make_dialog() {
this.dialog = new frappe.ui.Dialog({
title: __('Select Account'),
@ -276,6 +289,7 @@ class AccountSelectionDialog {
custom_filters: this.custom_filters.map(f => ({
field: f.field, operator: f.operator, value: f.value,
})),
link_filters: this.link_filters,
},
async: true,
callback: (r) => {