From 2a48fffe976757eff3e0b5bf6b841daff7a58cf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?= Date: Tue, 31 Mar 2026 10:45:03 +0800 Subject: [PATCH] =?UTF-8?q?=E6=90=9C=E7=B4=A2=E6=A1=86=E7=9B=B8=E4=BA=92?= =?UTF-8?q?=E8=BF=87=E6=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../models/receivable_filter_domains.py | 69 ++++++++++++++++ .../yuthon_report/receivable_report.js | 81 +++++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 yuthon_report/models/receivable_filter_domains.py diff --git a/yuthon_report/models/receivable_filter_domains.py b/yuthon_report/models/receivable_filter_domains.py new file mode 100644 index 000000000..d6618a41d --- /dev/null +++ b/yuthon_report/models/receivable_filter_domains.py @@ -0,0 +1,69 @@ +import datetime + +from odoo import api, models + + +class ReceivableModifyFilterDomains(models.Model): + _inherit = 'receivable.modify.line' + + @api.model + def get_receivable_filter_domains(self, params): + """根据当前筛选条件,返回物业/承租方/合同的候选 ID 集合,用于三个下拉框互相过滤。""" + domain = [ + ('receivable_id.is_invalid', '=', False), + ] + + date_start = params.get("date_start") + date_end = params.get("date_end") + if date_start: + domain.append(('receivable_id.due_date', '>=', datetime.datetime.fromisoformat(date_start).date())) + if date_end: + domain.append(('receivable_id.due_date', '<=', datetime.datetime.fromisoformat(date_end).date())) + + keyword = params.get("keyword") + if keyword: + domain += [ + '|', '|', + ('receivable_id.contract_id.contract_code', 'ilike', keyword), + ('receivable_id.property_name', 'ilike', keyword), + ('receivable_id.lessee_id.name', 'ilike', keyword), + ] + + condition = params.get('condition') + partner_id = params.get('partner_id') + company_id = params.get('company_id') + management_type_id = params.get('management_type_id') + property_id = params.get('property_id') + lessee_id = params.get('lessee_id') + contract_id = params.get('contract_id') + + if condition: + domain.append(('receivable_id.tenure_type', '=', condition)) + if partner_id: + domain.append(('receivable_id.precinct_id', '=', partner_id)) + if company_id: + domain.append(('receivable_id.company_id', '=', company_id)) + if property_id: + domain.append(('receivable_id.property_ids', 'in', [property_id])) + if lessee_id: + domain.append(('receivable_id.lessee_id', '=', lessee_id)) + if contract_id: + domain.append(('receivable_id.contract_id', '=', contract_id)) + if management_type_id: + domain.append(('receivable_id.management_type_id', '=', management_type_id)) + else: + # 与各报表保持一致:默认剔除押金、违约金类型 + domain.append(('receivable_id.management_type_id.name', 'not in', ['押金', '违约金'])) + + lines = self.sudo().search(domain) + receivables = lines.mapped('receivable_id') + + property_ids = set(receivables.mapped('property_ids').ids) + lessee_ids = set(receivables.mapped('lessee_id').ids) + contract_ids = set(receivables.mapped('contract_id').ids) + + return { + 'property_ids': list(property_ids), + 'lessee_ids': list(lessee_ids), + 'contract_ids': list(contract_ids), + } diff --git a/yuthon_report/static/src/components/yuthon_report/receivable_report.js b/yuthon_report/static/src/components/yuthon_report/receivable_report.js index 434ef7b5f..dc9e2dbc6 100644 --- a/yuthon_report/static/src/components/yuthon_report/receivable_report.js +++ b/yuthon_report/static/src/components/yuthon_report/receivable_report.js @@ -36,6 +36,10 @@ class ReceivableReport extends Component { selectProperty: {}, selectLessee: {}, selectContract: {}, + // 互相过滤用的候选 ID 集合(为 null 时表示不过滤) + allowedPropertyIds: null, + allowedLesseeIds: null, + allowedContractIds: null, condition: false, conditions: [ {'name': '', 'value': ''}, @@ -46,10 +50,16 @@ class ReceivableReport extends Component { stats: [] // 新增统计数据 }); + // 确保下拉框 domain 回调里的 this 指向当前组件实例 + this.getDomainProperty = this.getDomainProperty.bind(this); + this.getDomainLessee = this.getDomainLessee.bind(this); + this.getDomainContract = this.getDomainContract.bind(this); + this.config = this.props.action.context.report_config || {}; // dateMode: 'range'(默认,日期范围)或 'single'(单日期) this.dateMode = this.config.dateMode || 'range'; + onMounted(async () => { // 加载 ag-grid //await loadJS("https://cdn.jsdelivr.net/npm/ag-grid-community/dist/ag-grid-community.min.js"); @@ -235,6 +245,34 @@ class ReceivableReport extends Component { return []; } + // 物业名称下拉框 domain:根据当前互斥规则过滤 + getDomainProperty() { + const ids = this.state.allowedPropertyIds; + if (Array.isArray(ids) && ids.length) { + return [['id', 'in', ids]]; + } + // 返回空数组表示不过滤,由后端根据搜索词全局搜索 + return []; + } + + // 承租方下拉框 domain + getDomainLessee() { + const ids = this.state.allowedLesseeIds; + if (Array.isArray(ids) && ids.length) { + return [['id', 'in', ids]]; + } + return []; + } + + // 合同编号下拉框 domain + getDomainContract() { + const ids = this.state.allowedContractIds; + if (Array.isArray(ids) && ids.length) { + return [['id', 'in', ids]]; + } + return []; + } + getDomain() { console.log(this.state) const isCompanyEmpty = Object.keys(this.state.selectCompany).length === 0; @@ -245,6 +283,7 @@ class ReceivableReport extends Component { } } + async onUpdatePartner(v) { console.log(v) this.state.selectPartner = v[0] @@ -365,6 +404,23 @@ class ReceivableReport extends Component { [params] ); console.log(result) + + // 计算三个下拉框的候选集合,实现物业/承租方/合同的互相过滤 + try { + const domains = await this.serviceOrm.call( + this.config.model, + 'get_receivable_filter_domains', + [params] + ); + this.state.allowedPropertyIds = domains.property_ids || null; + this.state.allowedLesseeIds = domains.lessee_ids || null; + this.state.allowedContractIds = domains.contract_ids || null; + } catch (e) { + console.error('get_receivable_filter_domains failed:', e); + this.state.allowedPropertyIds = null; + this.state.allowedLesseeIds = null; + this.state.allowedContractIds = null; + } // 处理返回的数据结构 if (result && typeof result === 'object') { @@ -381,11 +437,36 @@ class ReceivableReport extends Component { this.grid.setGridOption('pinnedBottomRowData', []); this.state.stats = []; } + + // 数据加载完成后,自动调整所有列宽,等价于菜单里的“自动调整所有列” + if (this.grid) { + const columnApi = this.grid.columnApi && this.grid.columnApi; + if (columnApi && typeof columnApi.autoSizeAllColumns === 'function') { + try { + if (typeof columnApi.getAllColumns === 'function') { + const allCols = columnApi.getAllColumns() || []; + const allColIds = allCols.map(col => col.getColId()); + columnApi.autoSizeAllColumns(allColIds); + } else { + columnApi.autoSizeAllColumns(); + } + } catch (e) { + console.error('autoSizeAllColumns failed:', e); + } + } else if (typeof this.grid.autoSizeAllColumns === 'function') { + try { + this.grid.autoSizeAllColumns(); + } catch (e) { + console.error('grid.autoSizeAllColumns failed:', e); + } + } + } } // 数据刷新后重新应用收款类型过滤 this._applyManagementTypeFilter(); } + onExportExcel() { this.grid.exportDataAsExcel({ fileName: 'my-data.xlsx',