From 3dc04ed0927d9bbad30caa677608639618703131 Mon Sep 17 00:00:00 2001 From: Ali <010109ali@gmail.com> Date: Sun, 29 Mar 2026 17:10:37 +0400 Subject: [PATCH] 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) --- selections/api.py | 56 ++++++++++++++++++- .../public/js/account_selection_dialog.js | 14 +++++ 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/selections/api.py b/selections/api.py index 80a807f..d972b49 100644 --- a/selections/api.py +++ b/selections/api.py @@ -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 diff --git a/selections/public/js/account_selection_dialog.js b/selections/public/js/account_selection_dialog.js index ddf99ae..e2954ba 100644 --- a/selections/public/js/account_selection_dialog.js +++ b/selections/public/js/account_selection_dialog.js @@ -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) => {