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:
parent
3a3b850733
commit
3dc04ed092
|
|
@ -634,7 +634,7 @@ def add_items_to_document(doctype, docname, items):
|
||||||
# ────────────────────────────────────────────────────────────────────────────
|
# ────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@frappe.whitelist()
|
@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): Смещение для пагинации
|
start (int): Смещение для пагинации
|
||||||
page_length (int): Количество записей на странице
|
page_length (int): Количество записей на странице
|
||||||
custom_filters (list): Произвольные фильтры [{field, operator, value}]
|
custom_filters (list): Произвольные фильтры [{field, operator, value}]
|
||||||
|
link_filters (dict): Фильтры из set_query контрола-источника
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
dict: {items: [...], total_count: int}
|
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 {})
|
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 [])
|
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)
|
start = cint(start)
|
||||||
page_length = cint(page_length)
|
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)
|
.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()
|
search_term = cstr(filters.get('search_term', '')).strip()
|
||||||
if search_term:
|
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.disabled == 0)
|
||||||
.where(Account.is_group == 0)
|
.where(Account.is_group == 0)
|
||||||
)
|
)
|
||||||
|
if link_filters:
|
||||||
|
count_query = _apply_link_filters(count_query, Account, link_filters)
|
||||||
if search_term:
|
if search_term:
|
||||||
term = f"%{search_term}%"
|
term = f"%{search_term}%"
|
||||||
count_query = count_query.where(
|
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):
|
def _apply_account_custom_filters(query, Account, custom_filters):
|
||||||
"""
|
"""
|
||||||
Применить произвольные фильтры по полям DocType Account
|
Применить произвольные фильтры по полям DocType Account
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,23 @@ class AccountSelectionDialog {
|
||||||
this.custom_filters = [];
|
this.custom_filters = [];
|
||||||
this._account_fields = [];
|
this._account_fields = [];
|
||||||
this.accounts_data = [];
|
this.accounts_data = [];
|
||||||
|
this.link_filters = this._extract_link_filters();
|
||||||
|
|
||||||
this.make_dialog();
|
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() {
|
make_dialog() {
|
||||||
this.dialog = new frappe.ui.Dialog({
|
this.dialog = new frappe.ui.Dialog({
|
||||||
title: __('Select Account'),
|
title: __('Select Account'),
|
||||||
|
|
@ -276,6 +289,7 @@ class AccountSelectionDialog {
|
||||||
custom_filters: this.custom_filters.map(f => ({
|
custom_filters: this.custom_filters.map(f => ({
|
||||||
field: f.field, operator: f.operator, value: f.value,
|
field: f.field, operator: f.operator, value: f.value,
|
||||||
})),
|
})),
|
||||||
|
link_filters: this.link_filters,
|
||||||
},
|
},
|
||||||
async: true,
|
async: true,
|
||||||
callback: (r) => {
|
callback: (r) => {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue