diff --git a/yuthon_report/__manifest__.py b/yuthon_report/__manifest__.py index e5a6183d8..de75cdcfb 100644 --- a/yuthon_report/__manifest__.py +++ b/yuthon_report/__manifest__.py @@ -75,6 +75,8 @@ 'yuthon_report/static/src/components/yuthon_report/receivable_message.xml', 'yuthon_report/static/src/components/yuthon_report/yuthon_report.scss', 'yuthon_report/static/src/components/yuthon_report/receivable_report.js', + 'yuthon_report/static/src/components/yuthon_report/multi_select_autocomplete.js', + 'yuthon_report/static/src/components/yuthon_report/multi_select_autocomplete.xml', 'yuthon_report/static/src/components/yuthon_report/receivable_report.xml', 'yuthon_report/static/src/components/yuthon_report/receivable_flow_report.js', 'yuthon_report/static/src/components/yuthon_report/text_summary_report.js', diff --git a/yuthon_report/models/__init__.py b/yuthon_report/models/__init__.py index 494e0d0e8..425f095ec 100644 --- a/yuthon_report/models/__init__.py +++ b/yuthon_report/models/__init__.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from . import report_utils from . import res_partner from . import ledger_report from . import receivable_report diff --git a/yuthon_report/models/assessment_fee_monthly_report.py b/yuthon_report/models/assessment_fee_monthly_report.py index 8c0611fa0..3a922c3bd 100644 --- a/yuthon_report/models/assessment_fee_monthly_report.py +++ b/yuthon_report/models/assessment_fee_monthly_report.py @@ -3,6 +3,7 @@ import calendar import logging from odoo import api, models +from .report_utils import _id_list # 导入 ID 列表解析工具函数,支持多选参数 _logger = logging.getLogger(__name__) @@ -55,14 +56,14 @@ class PropertyBillsList(models.Model): ] # 公司/片区过滤 - company_id = params.get('company_id') - partner_id = params.get('partner_id') + company_id = _id_list(params.get('company_id')) + partner_id = _id_list(params.get('partner_id')) # 解析片区 ID 为整数列表(多选支持) if company_id: - domain.append(('lessor_id', '=', int(company_id))) + domain.append(('lessor_id', 'in', company_id)) if partner_id: domain += ['|', - ('contract_id.precinct_id', '=', int(partner_id)), - ('receivables_id.precinct_id', '=', int(partner_id)), + ('contract_id.precinct_id', 'in', partner_id), # 合同片区过滤(多选支持) + ('receivables_id.precinct_id', 'in', partner_id), # 应收记录片区过滤(多选支持) ] # 查询记录 diff --git a/yuthon_report/models/assessment_fee_yearly_report.py b/yuthon_report/models/assessment_fee_yearly_report.py index 0d5e1beb2..bdfc5d630 100644 --- a/yuthon_report/models/assessment_fee_yearly_report.py +++ b/yuthon_report/models/assessment_fee_yearly_report.py @@ -3,6 +3,7 @@ import calendar import logging from odoo import api, models +from .report_utils import _id_list # 导入 ID 列表解析工具函数,支持多选参数 _logger = logging.getLogger(__name__) @@ -55,14 +56,14 @@ class PropertyBillsList(models.Model): ] # 公司/片区过滤 - company_id = params.get('company_id') - partner_id = params.get('partner_id') + company_id = _id_list(params.get('company_id')) + partner_id = _id_list(params.get('partner_id')) # 解析片区 ID 为整数列表(多选支持) if company_id: - domain.append(('lessor_id', '=', int(company_id))) + domain.append(('lessor_id', 'in', company_id)) if partner_id: domain += ['|', - ('contract_id.precinct_id', '=', int(partner_id)), - ('receivables_id.precinct_id', '=', int(partner_id)), + ('contract_id.precinct_id', 'in', partner_id), # 合同片区过滤(多选支持) + ('receivables_id.precinct_id', 'in', partner_id), # 应收记录片区过滤(多选支持) ] # 查询记录 diff --git a/yuthon_report/models/att_report.py b/yuthon_report/models/att_report.py index 609214a9b..a628dd67f 100644 --- a/yuthon_report/models/att_report.py +++ b/yuthon_report/models/att_report.py @@ -1,5 +1,6 @@ import datetime +from .report_utils import _id_list from odoo import fields, models, api @@ -16,7 +17,7 @@ class AttendanceReport(models.Model): vacation_domain.append(("vacation_date", "<=", date_end)) salary_group_id = params.get('structure_id', False) department_id = params.get('department_id', False) - company_id = params.get('company_id', False) + company_id = _id_list(params.get('company_id', False)) employee_domain = [] if salary_group_id: @@ -24,7 +25,7 @@ class AttendanceReport(models.Model): if department_id: employee_domain.append(('department_id', '=', department_id)) if company_id: - employee_domain.append(('company_id', '=', company_id)) + employee_domain.append(('company_id', 'in', company_id)) employee_ids = [] if employee_domain: @@ -146,11 +147,13 @@ class AttendanceReport(models.Model): @api.model def get_domain(self, company_id=False): - if not company_id: + # 多选:company_id 标准化为列表 + cid_list = _id_list(company_id) + if not cid_list: return [], [] - department_records = self.env['hr.department'].search([('company_id', '=', company_id)]) + department_records = self.env['hr.department'].search([('company_id', 'in', cid_list)]) department_ids = [dept.id for dept in department_records] - salary_group_records = self.env['yuthon.salary.group'].search([('company_id', '=', company_id)]) + salary_group_records = self.env['yuthon.salary.group'].search([('company_id', 'in', cid_list)]) salary_group_ids = [struct.id for struct in salary_group_records] return department_ids, salary_group_ids diff --git a/yuthon_report/models/bank_statements_report.py b/yuthon_report/models/bank_statements_report.py index a72ec988f..11c1fc6ad 100644 --- a/yuthon_report/models/bank_statements_report.py +++ b/yuthon_report/models/bank_statements_report.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from .report_utils import _id_list from odoo import api, models @@ -23,9 +24,9 @@ class BankStatementsReport(models.Model): domain.append(('lending_direction', '=', 'loan')) # 公司搜索条件 - company_id = params.get('company_id') + company_id = _id_list(params.get('company_id')) if company_id: - domain.append(('company_id', '=', company_id)) + domain.append(('company_id', 'in', company_id)) # 账号单位搜索条件 your_own_unit = params.get('your_own_unit') diff --git a/yuthon_report/models/cum_arrears_report.py b/yuthon_report/models/cum_arrears_report.py index 0360f83b8..c3b891548 100644 --- a/yuthon_report/models/cum_arrears_report.py +++ b/yuthon_report/models/cum_arrears_report.py @@ -2,6 +2,7 @@ import calendar import datetime from odoo import api, models +from .report_utils import _id_list # 导入工具函数:将参数转换为列表格式,支持多选筛选 class CumArrearsReport(models.Model): @@ -50,8 +51,8 @@ class CumArrearsReport(models.Model): ] condition = params.get('condition') - partner_id = params.get('partner_id') - company_id = params.get('company_id') + partner_id = _id_list(params.get('partner_id')) # 将partner_id转换为列表,用于多选筛选 + company_id = _id_list(params.get('company_id')) management_type_id = params.get('management_type_id') property_id = params.get('property_id') lessee_id = params.get('lessee_id') @@ -60,9 +61,9 @@ class CumArrearsReport(models.Model): if condition: domain.append(('receivable_id.tenure_type', '=', condition)) if partner_id: - domain.append(('receivable_id.precinct_id', '=', partner_id)) + domain.append(('receivable_id.precinct_id', 'in', partner_id)) # 使用in操作符支持多选片区筛选 if company_id: - domain.append(('receivable_id.company_id', '=', company_id)) + domain.append(('receivable_id.company_id', 'in', company_id)) if property_id: domain.append(('receivable_id.property_ids', 'in', [property_id])) if lessee_id: diff --git a/yuthon_report/models/cum_income_report.py b/yuthon_report/models/cum_income_report.py index 2c46d64fb..d5bf42a49 100644 --- a/yuthon_report/models/cum_income_report.py +++ b/yuthon_report/models/cum_income_report.py @@ -2,6 +2,7 @@ import calendar import datetime from odoo import api, models +from .report_utils import _id_list # 导入工具函数:将参数转换为列表格式,支持多选筛选 class CumIncomeReport(models.Model): @@ -52,16 +53,16 @@ class CumIncomeReport(models.Model): ] condition = params.get('condition') - partner_id = params.get('partner_id') - company_id = params.get('company_id') + partner_id = _id_list(params.get('partner_id')) # 将partner_id转换为列表,用于多选筛选 + company_id = _id_list(params.get('company_id')) management_type_id = params.get('management_type_id') if condition: domain.append(('receivable_id.tenure_type', '=', condition)) if partner_id: - domain.append(('receivable_id.precinct_id', '=', partner_id)) + domain.append(('receivable_id.precinct_id', 'in', partner_id)) # 使用in操作符支持多选片区筛选 if company_id: - domain.append(('receivable_id.company_id', '=', company_id)) + domain.append(('receivable_id.company_id', 'in', company_id)) if management_type_id: domain.append(('receivable_id.management_type_id', '=', management_type_id)) else: diff --git a/yuthon_report/models/current_month_received_report.py b/yuthon_report/models/current_month_received_report.py index ae5246af2..894f89c69 100644 --- a/yuthon_report/models/current_month_received_report.py +++ b/yuthon_report/models/current_month_received_report.py @@ -1,6 +1,7 @@ import datetime from odoo import api, models +from .report_utils import _id_list # 导入工具函数:将参数转换为列表格式,支持多选筛选 class CurrentMonthReceivedReport(models.Model): @@ -39,8 +40,8 @@ class CurrentMonthReceivedReport(models.Model): ] condition = params.get('condition') - partner_id = params.get('partner_id') - company_id = params.get('company_id') + partner_id = _id_list(params.get('partner_id')) # 将partner_id转换为列表,用于多选筛选 + company_id = _id_list(params.get('company_id')) management_type_id = params.get('management_type_id') property_id = params.get('property_id') lessee_id = params.get('lessee_id') @@ -49,9 +50,9 @@ class CurrentMonthReceivedReport(models.Model): if condition: domain.append(('receivable_id.tenure_type', '=', condition)) if partner_id: - domain.append(('receivable_id.precinct_id', '=', partner_id)) + domain.append(('receivable_id.precinct_id', 'in', partner_id)) # 使用in操作符支持多选片区筛选 if company_id: - domain.append(('receivable_id.company_id', '=', company_id)) + domain.append(('receivable_id.company_id', 'in', company_id)) if property_id: domain.append(('receivable_id.property_ids', 'in', [property_id])) if lessee_id: diff --git a/yuthon_report/models/current_receivable_property_fee_report.py b/yuthon_report/models/current_receivable_property_fee_report.py index 1006def31..987ee601f 100644 --- a/yuthon_report/models/current_receivable_property_fee_report.py +++ b/yuthon_report/models/current_receivable_property_fee_report.py @@ -1,6 +1,7 @@ import datetime from odoo import api, models +from .report_utils import _id_list # 导入工具函数:将参数转换为列表格式,支持多选筛选 class CurrentReceivablePropertyFeeReport(models.Model): @@ -36,16 +37,16 @@ class CurrentReceivablePropertyFeeReport(models.Model): domain.append(("due_date", "<=", end_dt)) condition = params.get('condition') - partner_id = params.get('partner_id') - company_id = params.get('company_id') + partner_id = _id_list(params.get('partner_id')) # 将partner_id转换为列表,用于多选筛选 + company_id = _id_list(params.get('company_id')) management_type_id = params.get('management_type_id') if condition: domain.append(('tenure_type', '=', condition)) if partner_id: - domain.append(('precinct_id', '=', partner_id)) + domain.append(('precinct_id', 'in', partner_id)) # 使用in操作符支持多选片区筛选 if company_id: - domain.append(('company_id', '=', company_id)) + domain.append(('company_id', 'in', company_id)) if management_type_id: domain.append(('management_type_id', '=', management_type_id)) diff --git a/yuthon_report/models/deposit_cum_arrears_report.py b/yuthon_report/models/deposit_cum_arrears_report.py index 1f54dbcac..1d540e6f7 100644 --- a/yuthon_report/models/deposit_cum_arrears_report.py +++ b/yuthon_report/models/deposit_cum_arrears_report.py @@ -2,6 +2,7 @@ import datetime import calendar from odoo import api, models +from .report_utils import _id_list # partner_id 多选适配工具 class DepositCumArrearsReport(models.Model): @@ -49,17 +50,17 @@ class DepositCumArrearsReport(models.Model): ] condition = params.get('condition') - partner_id = params.get('partner_id') - company_id = params.get('company_id') + partner_id = _id_list(params.get('partner_id')) # 支持多选,转为列表 + company_id = _id_list(params.get('company_id')) property_id = params.get('property_id') lessee_id = params.get('lessee_id') contract_id = params.get('contract_id') if condition: domain.append(('tenure_type', '=', condition)) if partner_id: - domain.append(('precinct_id', '=', partner_id)) + domain.append(('precinct_id', 'in', partner_id)) # 多选用 in if company_id: - domain.append(('company_id', '=', company_id)) + domain.append(('company_id', 'in', company_id)) if property_id: domain.append(('property_ids', 'in', [property_id])) if lessee_id: @@ -142,13 +143,13 @@ class DepositCumArrearsReport(models.Model): ('is_invalid', '!=', True), ] - company_id = params.get('company_id') + company_id = _id_list(params.get('company_id')) property_id = params.get('property_id') lessee_id = params.get('lessee_id') contract_id = params.get('contract_id') if company_id: - domain.append(('company_id', '=', company_id)) + domain.append(('company_id', 'in', company_id)) if property_id: domain.append(('property_ids', 'in', [property_id])) if lessee_id: diff --git a/yuthon_report/models/deposit_month_receivable_report.py b/yuthon_report/models/deposit_month_receivable_report.py index d703226cd..c02275e07 100644 --- a/yuthon_report/models/deposit_month_receivable_report.py +++ b/yuthon_report/models/deposit_month_receivable_report.py @@ -2,6 +2,7 @@ import datetime import calendar from odoo import api, models +from .report_utils import _id_list # partner_id 多选适配工具 class DepositMonthReceivableReport(models.Model): @@ -39,14 +40,14 @@ class DepositMonthReceivableReport(models.Model): ] condition = params.get('condition') - partner_id = params.get('partner_id') - company_id = params.get('company_id') + partner_id = _id_list(params.get('partner_id')) # 支持多选,转为列表 + company_id = _id_list(params.get('company_id')) if condition: domain.append(('tenure_type', '=', condition)) if partner_id: - domain.append(('precinct_id', '=', partner_id)) + domain.append(('precinct_id', 'in', partner_id)) # 多选用 in if company_id: - domain.append(('company_id', '=', company_id)) + domain.append(('company_id', 'in', company_id)) lines = self.sudo().search(domain, order="contract_id asc") diff --git a/yuthon_report/models/deposit_month_received_report.py b/yuthon_report/models/deposit_month_received_report.py index 41c5993db..11515a967 100644 --- a/yuthon_report/models/deposit_month_received_report.py +++ b/yuthon_report/models/deposit_month_received_report.py @@ -2,6 +2,7 @@ import datetime import calendar from odoo import api, models +from .report_utils import _id_list # partner_id 多选适配工具 class DepositMonthReceivedReport(models.Model): @@ -44,17 +45,17 @@ class DepositMonthReceivedReport(models.Model): ] condition = params.get('condition') - partner_id = params.get('partner_id') - company_id = params.get('company_id') + partner_id = _id_list(params.get('partner_id')) # 支持多选,转为列表 + company_id = _id_list(params.get('company_id')) property_id = params.get('property_id') lessee_id = params.get('lessee_id') contract_id = params.get('contract_id') if condition: domain.append(('tenure_type', '=', condition)) if partner_id: - domain.append(('precinct_id', '=', partner_id)) + domain.append(('precinct_id', 'in', partner_id)) # 多选用 in if company_id: - domain.append(('company_id', '=', company_id)) + domain.append(('company_id', 'in', company_id)) if property_id: domain.append(('property_ids', 'in', [property_id])) if lessee_id: @@ -141,13 +142,13 @@ class DepositMonthReceivedReport(models.Model): ('is_invalid', '!=', True), ] - company_id = params.get('company_id') + company_id = _id_list(params.get('company_id')) property_id = params.get('property_id') lessee_id = params.get('lessee_id') contract_id = params.get('contract_id') if company_id: - domain.append(('company_id', '=', company_id)) + domain.append(('company_id', 'in', company_id)) if property_id: domain.append(('property_ids', 'in', [property_id])) if lessee_id: diff --git a/yuthon_report/models/deposit_month_refund_report.py b/yuthon_report/models/deposit_month_refund_report.py index 2fcd339c2..96960c141 100644 --- a/yuthon_report/models/deposit_month_refund_report.py +++ b/yuthon_report/models/deposit_month_refund_report.py @@ -2,6 +2,7 @@ import datetime import calendar from odoo import api, models +from .report_utils import _id_list # partner_id 多选适配工具 class DepositMonthRefundReport(models.Model): @@ -42,8 +43,8 @@ class DepositMonthRefundReport(models.Model): ] condition = params.get('condition') - partner_id = params.get('partner_id') - company_id = params.get('company_id') + partner_id = _id_list(params.get('partner_id')) # 支持多选,转为列表 + company_id = _id_list(params.get('company_id')) management_type_id = params.get('management_type_id') property_id = params.get('property_id') lessee_id = params.get('lessee_id') @@ -51,9 +52,9 @@ class DepositMonthRefundReport(models.Model): if condition: domain.append(('tenure_type', '=', condition)) if partner_id: - domain.append(('precinct_id', '=', partner_id)) + domain.append(('precinct_id', 'in', partner_id)) # 多选用 in if company_id: - domain.append(('company_id', '=', company_id)) + domain.append(('company_id', 'in', company_id)) if property_id: domain.append(('property_ids', 'in', [property_id])) if lessee_id: diff --git a/yuthon_report/models/deposit_pre_received_report.py b/yuthon_report/models/deposit_pre_received_report.py index 97c6373ea..30ed0ba45 100644 --- a/yuthon_report/models/deposit_pre_received_report.py +++ b/yuthon_report/models/deposit_pre_received_report.py @@ -2,6 +2,7 @@ import datetime import calendar from odoo import api, models +from .report_utils import _id_list # partner_id 多选适配工具 class DepositPreReceivedReport(models.Model): @@ -44,17 +45,17 @@ class DepositPreReceivedReport(models.Model): ] condition = params.get('condition') - partner_id = params.get('partner_id') - company_id = params.get('company_id') + partner_id = _id_list(params.get('partner_id')) # 支持多选,转为列表 + company_id = _id_list(params.get('company_id')) property_id = params.get('property_id') lessee_id = params.get('lessee_id') contract_id = params.get('contract_id') if condition: domain.append(('tenure_type', '=', condition)) if partner_id: - domain.append(('precinct_id', '=', partner_id)) + domain.append(('precinct_id', 'in', partner_id)) # 多选用 in if company_id: - domain.append(('company_id', '=', company_id)) + domain.append(('company_id', 'in', company_id)) if property_id: domain.append(('property_ids', 'in', [property_id])) if lessee_id: @@ -157,13 +158,13 @@ class DepositPreReceivedReport(models.Model): ('is_invalid', '!=', True), ] - company_id = params.get('company_id') + company_id = _id_list(params.get('company_id')) property_id = params.get('property_id') lessee_id = params.get('lessee_id') contract_id = params.get('contract_id') if company_id: - domain.append(('company_id', '=', company_id)) + domain.append(('company_id', 'in', company_id)) if property_id: domain.append(('property_ids', 'in', [property_id])) if lessee_id: diff --git a/yuthon_report/models/deposit_total_report.py b/yuthon_report/models/deposit_total_report.py index 71180e047..0a2d5322a 100644 --- a/yuthon_report/models/deposit_total_report.py +++ b/yuthon_report/models/deposit_total_report.py @@ -2,6 +2,7 @@ import datetime import calendar from odoo import api, models +from .report_utils import _id_list # partner_id 多选适配工具 class DepositTotalReport(models.Model): @@ -59,8 +60,8 @@ class DepositTotalReport(models.Model): ] condition = params.get('condition') - partner_id = params.get('partner_id') - company_id = params.get('company_id') + partner_id = _id_list(params.get('partner_id')) # 支持多选,转为列表 + company_id = _id_list(params.get('company_id')) management_type_id = params.get('management_type_id') property_id = params.get('property_id') lessee_id = params.get('lessee_id') @@ -68,9 +69,9 @@ class DepositTotalReport(models.Model): if condition: domain.append(('tenure_type', '=', condition)) if partner_id: - domain.append(('precinct_id', '=', partner_id)) + domain.append(('precinct_id', 'in', partner_id)) # 多选用 in if company_id: - domain.append(('company_id', '=', company_id)) + domain.append(('company_id', 'in', company_id)) if property_id: domain.append(('property_ids', 'in', [property_id])) if lessee_id: diff --git a/yuthon_report/models/leased_area_report.py b/yuthon_report/models/leased_area_report.py index 62eab111a..dc4587a53 100644 --- a/yuthon_report/models/leased_area_report.py +++ b/yuthon_report/models/leased_area_report.py @@ -3,6 +3,7 @@ import datetime import logging from odoo import api, models +from .report_utils import _id_list # 导入 ID 列表解析工具函数,支持多选参数 _logger = logging.getLogger(__name__) @@ -41,8 +42,8 @@ class LeasedAreaReport(models.Model): keyword = params.get("keyword") condition = params.get('condition') - partner_id = params.get('partner_id') - company_id = params.get('company_id') + partner_id = _id_list(params.get('partner_id')) # 解析片区 ID 为整数列表(多选支持) + company_id = _id_list(params.get('company_id')) # 前端侧边栏下拉选择器传入的 ID property_id = params.get('property_id') lessee_id = params.get('lessee_id') @@ -202,7 +203,7 @@ class LeasedAreaReport(models.Model): matched = False for cl in lines: ml = manage_map.get(cl.address_id.id) if cl.address_id else None - if ml and ml.precinct_id and ml.precinct_id.id == int(partner_id): + if ml and ml.precinct_id and ml.precinct_id.id in partner_id: # 片区匹配(多选支持) matched = True break if not matched: @@ -211,7 +212,7 @@ class LeasedAreaReport(models.Model): matched = False for cl in lines: ml = manage_map.get(cl.address_id.id) if cl.address_id else None - if ml and ml.company_id and ml.company_id.id == int(company_id): + if ml and ml.company_id and ml.company_id.id in company_id: matched = True break if not matched: diff --git a/yuthon_report/models/ledger_report.py b/yuthon_report/models/ledger_report.py index 497005e9f..c27991b79 100644 --- a/yuthon_report/models/ledger_report.py +++ b/yuthon_report/models/ledger_report.py @@ -2,6 +2,7 @@ import datetime import calendar from odoo import fields, models, api +from .report_utils import _id_list # partner_id 多选适配工具 class YuthonProperty(models.Model): @@ -32,16 +33,16 @@ class YuthonProperty(models.Model): # 基础过滤条件 condition = params.get('condition', False) - partner_id = params.get('partner_id', False) - company_id = params.get('company_id', False) + partner_id = _id_list(params.get('partner_id')) # 支持多选,转为列表 + company_id = _id_list(params.get('company_id', False)) domain = [] if condition: domain.append(('tenure_type', '=', condition)) if partner_id: - domain.append(('precinct_id', '=', partner_id)) + domain.append(('precinct_id', 'in', partner_id)) # 多选用 in if company_id: - domain.append(('company_id', '=', company_id)) + domain.append(('company_id', 'in', company_id)) properties = self.search(domain, limit=500) diff --git a/yuthon_report/models/maintenance_fee_monthly_report.py b/yuthon_report/models/maintenance_fee_monthly_report.py index 114a7f364..15b5c3537 100644 --- a/yuthon_report/models/maintenance_fee_monthly_report.py +++ b/yuthon_report/models/maintenance_fee_monthly_report.py @@ -3,6 +3,7 @@ import calendar import logging from odoo import api, models +from .report_utils import _id_list # 导入 ID 列表解析工具函数,支持多选参数 _logger = logging.getLogger(__name__) @@ -55,14 +56,14 @@ class PropertyBillsList(models.Model): ] # 公司/片区过滤 - company_id = params.get('company_id') - partner_id = params.get('partner_id') + company_id = _id_list(params.get('company_id')) + partner_id = _id_list(params.get('partner_id')) # 解析片区 ID 为整数列表(多选支持) if company_id: - domain.append(('lessor_id', '=', int(company_id))) + domain.append(('lessor_id', 'in', company_id)) if partner_id: domain += ['|', - ('contract_id.precinct_id', '=', int(partner_id)), - ('receivables_id.precinct_id', '=', int(partner_id)), + ('contract_id.precinct_id', 'in', partner_id), # 合同片区过滤(多选支持) + ('receivables_id.precinct_id', 'in', partner_id), # 应收记录片区过滤(多选支持) ] # 查询记录 diff --git a/yuthon_report/models/maintenance_fee_yearly_report.py b/yuthon_report/models/maintenance_fee_yearly_report.py index 7f2e2cd0f..b915ac4d6 100644 --- a/yuthon_report/models/maintenance_fee_yearly_report.py +++ b/yuthon_report/models/maintenance_fee_yearly_report.py @@ -3,6 +3,7 @@ import calendar import logging from odoo import api, models +from .report_utils import _id_list # 导入 ID 列表解析工具函数,支持多选参数 _logger = logging.getLogger(__name__) @@ -55,14 +56,14 @@ class PropertyBillsList(models.Model): ] # 公司/片区过滤 - company_id = params.get('company_id') - partner_id = params.get('partner_id') + company_id = _id_list(params.get('company_id')) + partner_id = _id_list(params.get('partner_id')) # 解析片区 ID 为整数列表(多选支持) if company_id: - domain.append(('lessor_id', '=', int(company_id))) + domain.append(('lessor_id', 'in', company_id)) if partner_id: domain += ['|', - ('contract_id.precinct_id', '=', int(partner_id)), - ('receivables_id.precinct_id', '=', int(partner_id)), + ('contract_id.precinct_id', 'in', partner_id), # 合同片区过滤(多选支持) + ('receivables_id.precinct_id', 'in', partner_id), # 应收记录片区过滤(多选支持) ] # 查询记录 diff --git a/yuthon_report/models/management_area_report.py b/yuthon_report/models/management_area_report.py index c1b53645d..d409d17cf 100644 --- a/yuthon_report/models/management_area_report.py +++ b/yuthon_report/models/management_area_report.py @@ -2,6 +2,7 @@ import datetime import logging from odoo import api, models +from .report_utils import _id_list # partner_id 多选适配工具 _logger = logging.getLogger(__name__) @@ -33,8 +34,8 @@ class ManagementAreaReport(models.Model): keyword = params.get("keyword") condition = params.get('condition') - partner_id = params.get('partner_id') - company_id = params.get('company_id') + partner_id = _id_list(params.get('partner_id')) # 支持多选,转为列表 + company_id = _id_list(params.get('company_id')) # 前端侧边栏下拉选择器传入的 ID property_id = params.get('property_id') lessee_id = params.get('lessee_id') @@ -65,9 +66,9 @@ class ManagementAreaReport(models.Model): if condition: domain.append(('tenure_type', '=', condition)) if partner_id: - domain.append(('precinct_id', '=', partner_id)) + domain.append(('precinct_id', 'in', partner_id)) # 多选用 in if company_id: - domain.append(('company_id', '=', company_id)) + domain.append(('company_id', 'in', company_id)) # 物业名称下拉选择器:直接过滤 yuthon_property_id if property_id: domain.append(('yuthon_property_id', '=', int(property_id))) diff --git a/yuthon_report/models/management_fee_monthly_report.py b/yuthon_report/models/management_fee_monthly_report.py index 8c3da4ef1..20ae5804e 100644 --- a/yuthon_report/models/management_fee_monthly_report.py +++ b/yuthon_report/models/management_fee_monthly_report.py @@ -3,6 +3,7 @@ import calendar import logging from odoo import api, models +from .report_utils import _id_list # 导入 ID 列表解析工具函数,支持多选参数 _logger = logging.getLogger(__name__) @@ -55,14 +56,14 @@ class PropertyBillsList(models.Model): ] # 公司/片区过滤 - company_id = params.get('company_id') - partner_id = params.get('partner_id') + company_id = _id_list(params.get('company_id')) + partner_id = _id_list(params.get('partner_id')) # 解析片区 ID 为整数列表(多选支持) if company_id: - domain.append(('lessor_id', '=', int(company_id))) + domain.append(('lessor_id', 'in', company_id)) if partner_id: domain += ['|', - ('contract_id.precinct_id', '=', int(partner_id)), - ('receivables_id.precinct_id', '=', int(partner_id)), + ('contract_id.precinct_id', 'in', partner_id), # 合同片区过滤(多选支持) + ('receivables_id.precinct_id', 'in', partner_id), # 应收记录片区过滤(多选支持) ] # 查询记录 diff --git a/yuthon_report/models/management_fee_yearly_report.py b/yuthon_report/models/management_fee_yearly_report.py index de6a17fc0..9874a5e57 100644 --- a/yuthon_report/models/management_fee_yearly_report.py +++ b/yuthon_report/models/management_fee_yearly_report.py @@ -3,6 +3,7 @@ import calendar import logging from odoo import api, models +from .report_utils import _id_list # 导入 ID 列表解析工具函数,支持多选参数 _logger = logging.getLogger(__name__) @@ -55,14 +56,14 @@ class PropertyBillsList(models.Model): ] # 公司/片区过滤 - company_id = params.get('company_id') - partner_id = params.get('partner_id') + company_id = _id_list(params.get('company_id')) + partner_id = _id_list(params.get('partner_id')) # 解析片区 ID 为整数列表(多选支持) if company_id: - domain.append(('lessor_id', '=', int(company_id))) + domain.append(('lessor_id', 'in', company_id)) if partner_id: domain += ['|', - ('contract_id.precinct_id', '=', int(partner_id)), - ('receivables_id.precinct_id', '=', int(partner_id)), + ('contract_id.precinct_id', 'in', partner_id), # 合同片区过滤(多选支持) + ('receivables_id.precinct_id', 'in', partner_id), # 应收记录片区过滤(多选支持) ] # 查询记录 diff --git a/yuthon_report/models/month_receivable_report.py b/yuthon_report/models/month_receivable_report.py index 5c8455ea5..10ee8ea5b 100644 --- a/yuthon_report/models/month_receivable_report.py +++ b/yuthon_report/models/month_receivable_report.py @@ -2,6 +2,7 @@ import datetime import calendar from odoo import api, models +from .report_utils import _id_list # 导入工具函数:将参数转换为列表格式,支持多选筛选 class MonthReceivableReport(models.Model): @@ -38,8 +39,8 @@ class MonthReceivableReport(models.Model): ('receivable_id.contract_id.contract_code', 'ilike', keyword)] condition = params.get('condition') - partner_id = params.get('partner_id') - company_id = params.get('company_id') + partner_id = _id_list(params.get('partner_id')) # 将partner_id转换为列表,用于多选筛选 + company_id = _id_list(params.get('company_id')) management_type_id = params.get('management_type_id') property_id = params.get('property_id') lessee_id = params.get('lessee_id') @@ -48,9 +49,9 @@ class MonthReceivableReport(models.Model): if condition: domain.append(('receivable_id.tenure_type', '=', condition)) if partner_id: - domain.append(('receivable_id.precinct_id', '=', partner_id)) + domain.append(('receivable_id.precinct_id', 'in', partner_id)) # 使用in操作符支持多选片区筛选 if company_id: - domain.append(('receivable_id.company_id', '=', company_id)) + domain.append(('receivable_id.company_id', 'in', company_id)) if property_id: domain.append(('receivable_id.property_ids', 'in', [property_id])) if lessee_id: diff --git a/yuthon_report/models/monthly_precollection_report.py b/yuthon_report/models/monthly_precollection_report.py index d02ec33b4..5ef11be41 100644 --- a/yuthon_report/models/monthly_precollection_report.py +++ b/yuthon_report/models/monthly_precollection_report.py @@ -2,6 +2,7 @@ import datetime import calendar from odoo import api, models +from .report_utils import _id_list # 导入工具函数:将参数转换为列表格式,支持多选筛选 class MonthlyPrecollectionReport(models.Model): @@ -43,8 +44,8 @@ class MonthlyPrecollectionReport(models.Model): ] condition = params.get('condition') - partner_id = params.get('partner_id') - company_id = params.get('company_id') + partner_id = _id_list(params.get('partner_id')) # 将partner_id转换为列表,用于多选筛选 + company_id = _id_list(params.get('company_id')) management_type_id = params.get('management_type_id') property_id = params.get('property_id') lessee_id = params.get('lessee_id') @@ -53,9 +54,9 @@ class MonthlyPrecollectionReport(models.Model): if condition: domain.append(('receivable_id.tenure_type', '=', condition)) if partner_id: - domain.append(('receivable_id.precinct_id', '=', partner_id)) + domain.append(('receivable_id.precinct_id', 'in', partner_id)) # 使用in操作符支持多选片区筛选 if company_id: - domain.append(('receivable_id.company_id', '=', company_id)) + domain.append(('receivable_id.company_id', 'in', company_id)) if property_id: domain.append(('receivable_id.property_ids', 'in', [property_id])) if lessee_id: diff --git a/yuthon_report/models/parking_fee_current_month_receivable.py b/yuthon_report/models/parking_fee_current_month_receivable.py index fb737e261..06d96eb1e 100644 --- a/yuthon_report/models/parking_fee_current_month_receivable.py +++ b/yuthon_report/models/parking_fee_current_month_receivable.py @@ -1,6 +1,7 @@ import datetime from odoo import api, models +from .report_utils import _id_list # partner_id 多选适配工具 class ParkingFeeCurrentMonthReceivable(models.Model): @@ -46,16 +47,16 @@ class ParkingFeeCurrentMonthReceivable(models.Model): # 其他过滤 condition = params.get('condition') - partner_id = params.get('partner_id') - company_id = params.get('company_id') + partner_id = _id_list(params.get('partner_id')) # 支持多选,转为列表 + company_id = _id_list(params.get('company_id')) management_type_id = params.get('management_type_id') if condition: domain.append(('tenure_type', '=', condition)) if partner_id: - domain.append(('precinct_id', '=', partner_id)) + domain.append(('precinct_id', 'in', partner_id)) # 多选用 in if company_id: - domain.append(('company_id', '=', company_id)) + domain.append(('company_id', 'in', company_id)) # 管理类型 if management_type_id: diff --git a/yuthon_report/models/parking_fee_current_month_report.py b/yuthon_report/models/parking_fee_current_month_report.py index e4a82d17c..5a6951c26 100644 --- a/yuthon_report/models/parking_fee_current_month_report.py +++ b/yuthon_report/models/parking_fee_current_month_report.py @@ -1,6 +1,7 @@ import datetime from odoo import api, models +from .report_utils import _id_list # partner_id 多选适配工具 class ParkingFeeCurrentMonthReport(models.Model): @@ -41,16 +42,16 @@ class ParkingFeeCurrentMonthReport(models.Model): end_dt = datetime.datetime.fromisoformat(date_end).date() if date_end else month_end condition = params.get('condition') - partner_id = params.get('partner_id') - company_id = params.get('company_id') + partner_id = _id_list(params.get('partner_id')) # 支持多选,转为列表 + company_id = _id_list(params.get('company_id')) management_type_id = params.get('management_type_id') if condition: domain.append(('tenure_type', '=', condition)) if partner_id: - domain.append(('precinct_id', '=', partner_id)) + domain.append(('precinct_id', 'in', partner_id)) # 多选用 in if company_id: - domain.append(('company_id', '=', company_id)) + domain.append(('company_id', 'in', company_id)) # 管理类型 if management_type_id: diff --git a/yuthon_report/models/parking_fee_yearly_report.py b/yuthon_report/models/parking_fee_yearly_report.py index da3389de1..58c7735ab 100644 --- a/yuthon_report/models/parking_fee_yearly_report.py +++ b/yuthon_report/models/parking_fee_yearly_report.py @@ -1,6 +1,7 @@ import datetime from odoo import api, models +from .report_utils import _id_list # partner_id 多选适配工具 class ParkingFeeYearlyReport(models.Model): @@ -42,16 +43,16 @@ class ParkingFeeYearlyReport(models.Model): # 过滤条件 condition = params.get('condition') - partner_id = params.get('partner_id') - company_id = params.get('company_id') + partner_id = _id_list(params.get('partner_id')) # 支持多选,转为列表 + company_id = _id_list(params.get('company_id')) management_type_id = params.get('management_type_id') if condition: domain.append(('tenure_type', '=', condition)) if partner_id: - domain.append(('precinct_id', '=', partner_id)) + domain.append(('precinct_id', 'in', partner_id)) # 多选用 in if company_id: - domain.append(('company_id', '=', company_id)) + domain.append(('company_id', 'in', company_id)) if management_type_id: domain.append(('management_type_id', '=', management_type_id)) diff --git a/yuthon_report/models/penalty_arrears_report.py b/yuthon_report/models/penalty_arrears_report.py index 43b2f5a4c..1e978c7c7 100644 --- a/yuthon_report/models/penalty_arrears_report.py +++ b/yuthon_report/models/penalty_arrears_report.py @@ -2,6 +2,7 @@ import datetime # 日期时间处理模块,用于日期解析、计算、格 import logging # 日志模块,用于调试输出 from odoo import api, models # Odoo 核心 API:api 装饰器、models 模型基类 +from .report_utils import _id_list # 导入 ID 列表解析工具函数,支持多选参数 _logger = logging.getLogger(__name__) # 获取当前模块的日志记录器 @@ -42,8 +43,8 @@ class PenaltyArrearsReport(models.Model): # ========== 二、构建公共筛选条件 ========== keyword = params.get("keyword") # 关键字搜索 condition = params.get('condition') # 租赁类型 - partner_id = params.get('partner_id') # 片区 ID - company_id = params.get('company_id') # 公司 ID + partner_id = _id_list(params.get('partner_id')) # 解析片区 ID 为整数列表(多选支持) + company_id = _id_list(params.get('company_id')) # 公司 ID property_id = params.get('property_id') # 物业 ID lessee_id = params.get('lessee_id') # 承租方 ID contract_id = params.get('contract_id') # 合同 ID @@ -64,9 +65,9 @@ class PenaltyArrearsReport(models.Model): return False if condition and rec.tenure_type != condition: return False - if partner_id and rec.precinct_id.id != int(partner_id): + if partner_id and rec.precinct_id.id not in partner_id: # 片区过滤(多选支持) return False - if company_id and rec.company_id.id != int(company_id): + if company_id and rec.company_id.id not in company_id: return False if property_id and int(property_id) not in (rec.property_ids.ids or []): return False diff --git a/yuthon_report/models/penalty_month_received_report.py b/yuthon_report/models/penalty_month_received_report.py index 5e9915009..4e37228b2 100644 --- a/yuthon_report/models/penalty_month_received_report.py +++ b/yuthon_report/models/penalty_month_received_report.py @@ -3,6 +3,7 @@ import logging # 日志模块,用于调试输出 import re # 正则表达式模块,用于解析收费区间字符串 from odoo import api, models # Odoo 核心 API:api 装饰器、models 模型基类 +from .report_utils import _id_list # 导入 ID 列表解析工具函数,支持多选参数 _logger = logging.getLogger(__name__) # 获取当前模块的日志记录器 @@ -42,8 +43,8 @@ class PenaltyMonthReceivedReport(models.Model): # 其他筛选条件参数 keyword = params.get("keyword") condition = params.get('condition') - partner_id = params.get('partner_id') - company_id = params.get('company_id') + partner_id = _id_list(params.get('partner_id')) # 解析片区 ID 为整数列表(多选支持) + company_id = _id_list(params.get('company_id')) property_id = params.get('property_id') lessee_id = params.get('lessee_id') contract_id = params.get('contract_id') @@ -64,9 +65,9 @@ class PenaltyMonthReceivedReport(models.Model): return False if condition and rec.tenure_type != condition: return False - if partner_id and rec.precinct_id.id != int(partner_id): + if partner_id and rec.precinct_id.id not in partner_id: # 片区过滤(多选支持) return False - if company_id and rec.company_id.id != int(company_id): + if company_id and rec.company_id.id not in company_id: return False if property_id and int(property_id) not in (rec.property_ids.ids or []): return False @@ -90,9 +91,9 @@ class PenaltyMonthReceivedReport(models.Model): match = True if not match: return False - if partner_id and ml.precinct_id and ml.precinct_id.id != int(partner_id): + if partner_id and ml.precinct_id and ml.precinct_id.id not in partner_id: # modify.line 片区过滤(多选支持) return False - if company_id and ml.company_id and ml.company_id.id != int(company_id): + if company_id and ml.company_id and ml.company_id.id not in company_id: return False if lessee_id and (not ml.lessee_id or ml.lessee_id.id != int(lessee_id)): return False diff --git a/yuthon_report/models/penalty_year_cum_report.py b/yuthon_report/models/penalty_year_cum_report.py index 4fc19c3b5..f417a5810 100644 --- a/yuthon_report/models/penalty_year_cum_report.py +++ b/yuthon_report/models/penalty_year_cum_report.py @@ -3,6 +3,7 @@ import logging # 日志模块,用于调试输出 import re # 正则表达式模块,用于解析收费区间字符串 from odoo import api, models # Odoo 核心 API:api 装饰器、models 模型基类 +from .report_utils import _id_list # 导入 ID 列表解析工具函数,支持多选参数 _logger = logging.getLogger(__name__) # 获取当前模块的日志记录器 @@ -44,8 +45,8 @@ class PenaltyYearCumReport(models.Model): # 其他筛选条件参数 keyword = params.get("keyword") condition = params.get('condition') - partner_id = params.get('partner_id') - company_id = params.get('company_id') + partner_id = _id_list(params.get('partner_id')) # 解析片区 ID 为整数列表(多选支持) + company_id = _id_list(params.get('company_id')) property_id = params.get('property_id') lessee_id = params.get('lessee_id') contract_id = params.get('contract_id') @@ -66,9 +67,9 @@ class PenaltyYearCumReport(models.Model): return False if condition and rec.tenure_type != condition: return False - if partner_id and rec.precinct_id.id != int(partner_id): + if partner_id and rec.precinct_id.id not in partner_id: # 片区过滤(多选支持) return False - if company_id and rec.company_id.id != int(company_id): + if company_id and rec.company_id.id not in company_id: return False if property_id and int(property_id) not in (rec.property_ids.ids or []): return False @@ -92,9 +93,9 @@ class PenaltyYearCumReport(models.Model): match = True if not match: return False - if partner_id and ml.precinct_id and ml.precinct_id.id != int(partner_id): + if partner_id and ml.precinct_id and ml.precinct_id.id not in partner_id: # modify.line 片区过滤(多选支持) return False - if company_id and ml.company_id and ml.company_id.id != int(company_id): + if company_id and ml.company_id and ml.company_id.id not in company_id: return False if lessee_id and (not ml.lessee_id or ml.lessee_id.id != int(lessee_id)): return False diff --git a/yuthon_report/models/pre_advance_report.py b/yuthon_report/models/pre_advance_report.py index b051ee6c8..012c59121 100644 --- a/yuthon_report/models/pre_advance_report.py +++ b/yuthon_report/models/pre_advance_report.py @@ -1,6 +1,7 @@ import datetime from odoo import api, models +from .report_utils import _id_list # 导入工具函数:将参数转换为列表格式,支持多选筛选 class PreAdvanceReport(models.Model): @@ -50,8 +51,8 @@ class PreAdvanceReport(models.Model): ] condition = params.get('condition') - partner_id = params.get('partner_id') - company_id = params.get('company_id') + partner_id = _id_list(params.get('partner_id')) # 将partner_id转换为列表,用于多选筛选 + company_id = _id_list(params.get('company_id')) management_type_id = params.get('management_type_id') property_id = params.get('property_id') lessee_id = params.get('lessee_id') @@ -60,9 +61,9 @@ class PreAdvanceReport(models.Model): if condition: domain.append(('receivable_id.tenure_type', '=', condition)) if partner_id: - domain.append(('receivable_id.precinct_id', '=', partner_id)) + domain.append(('receivable_id.precinct_id', 'in', partner_id)) # 使用in操作符支持多选片区筛选 if company_id: - domain.append(('receivable_id.company_id', '=', company_id)) + domain.append(('receivable_id.company_id', 'in', company_id)) if property_id: domain.append(('receivable_id.property_ids', 'in', [property_id])) if lessee_id: diff --git a/yuthon_report/models/pre_arrears_report.py b/yuthon_report/models/pre_arrears_report.py index 5f65ba133..b6d13a49b 100644 --- a/yuthon_report/models/pre_arrears_report.py +++ b/yuthon_report/models/pre_arrears_report.py @@ -2,6 +2,7 @@ import datetime import logging from odoo import api, models +from .report_utils import _id_list _logger = logging.getLogger(__name__) @@ -40,8 +41,9 @@ class PreArrearsReport(models.Model): # 提取筛选参数 keyword = params.get("keyword") condition = params.get('condition') - partner_id = params.get('partner_id') - company_id = params.get('company_id') + # 多选标准化:partner_id 统一为列表 + partner_id = _id_list(params.get('partner_id')) + company_id = _id_list(params.get('company_id')) management_type_id = params.get('management_type_id') property_id = params.get('property_id') lessee_id = params.get('lessee_id') @@ -67,9 +69,10 @@ class PreArrearsReport(models.Model): if condition: domain.append(('receivable_id.tenure_type', '=', condition)) if partner_id: - domain.append(('receivable_id.precinct_id', '=', partner_id)) + # 多选:片区管理员用 'in' 操作符 + domain.append(('receivable_id.precinct_id', 'in', partner_id)) if company_id: - domain.append(('receivable_id.company_id', '=', company_id)) + domain.append(('receivable_id.company_id', 'in', company_id)) if property_id: domain.append(('receivable_id.property_ids', 'in', [property_id])) if lessee_id: @@ -151,9 +154,10 @@ class PreArrearsReport(models.Model): if condition: receivable_domain.append(('tenure_type', '=', condition)) if partner_id: - receivable_domain.append(('precinct_id', '=', partner_id)) + # 多选:片区管理员用 'in' 操作符 + receivable_domain.append(('precinct_id', 'in', partner_id)) if company_id: - receivable_domain.append(('company_id', '=', company_id)) + receivable_domain.append(('company_id', 'in', company_id)) if property_id: receivable_domain.append(('property_ids', 'in', [property_id])) if lessee_id: diff --git a/yuthon_report/models/receivable_flow_report.py b/yuthon_report/models/receivable_flow_report.py index 491395485..9b2f103de 100644 --- a/yuthon_report/models/receivable_flow_report.py +++ b/yuthon_report/models/receivable_flow_report.py @@ -2,6 +2,7 @@ import datetime import re from odoo import api, models +from .report_utils import _id_list # 导入工具函数:将参数转换为列表格式,支持多选筛选 class ReceivableFlowReport(models.Model): @@ -20,13 +21,13 @@ class ReceivableFlowReport(models.Model): ('is_invalid', '!=', True), ] - company_id = params.get('company_id') + company_id = _id_list(params.get('company_id')) property_id = params.get('property_id') lessee_id = params.get('lessee_id') contract_id = params.get('contract_id') if company_id: - domain.append(('company_id', '=', company_id)) + domain.append(('company_id', 'in', company_id)) if property_id: domain.append(('property_ids', 'in', [property_id])) if lessee_id: @@ -74,8 +75,8 @@ class ReceivableFlowReport(models.Model): ] condition = params.get('condition') - partner_id = params.get('partner_id') - company_id = params.get('company_id') + partner_id = _id_list(params.get('partner_id')) # 将partner_id转换为列表,用于多选筛选 + company_id = _id_list(params.get('company_id')) property_id = params.get('property_id') lessee_id = params.get('lessee_id') contract_id = params.get('contract_id') @@ -83,9 +84,9 @@ class ReceivableFlowReport(models.Model): if condition: domain.append(('tenure_type', '=', condition)) if partner_id: - domain.append(('precinct_id', '=', partner_id)) + domain.append(('precinct_id', 'in', partner_id)) # 使用in操作符支持多选片区筛选 if company_id: - domain.append(('company_id', '=', company_id)) + domain.append(('company_id', 'in', company_id)) if property_id: domain.append(('property_ids', 'in', [property_id])) if lessee_id: diff --git a/yuthon_report/models/receivable_report.py b/yuthon_report/models/receivable_report.py index bf915c926..1377fb648 100644 --- a/yuthon_report/models/receivable_report.py +++ b/yuthon_report/models/receivable_report.py @@ -1,6 +1,7 @@ import datetime from odoo import fields, models, api +from .report_utils import _id_list # 导入工具函数:将参数转换为列表格式,支持多选筛选 class YuthonProperty(models.Model): @@ -63,15 +64,15 @@ class YuthonProperty(models.Model): domain.append(("due_date", "<=", date_end)) condition = params.get('condition', False) - partner_id = params.get('partner_id', False) - company_id = params.get('company_id', False) + partner_id = _id_list(params.get('partner_id', False)) # 将partner_id转换为列表,用于多选筛选 + company_id = _id_list(params.get('company_id', False)) if condition: domain.append(('tenure_type', '=', condition)) if partner_id: - domain.append(('precinct_id', '=', partner_id)) + domain.append(('precinct_id', 'in', partner_id)) # 使用in操作符支持多选片区筛选 if company_id: - domain.append(('company_id', '=', company_id)) + domain.append(('company_id', 'in', company_id)) receivables = self.search(domain, order="contract_id asc, due_date asc") @@ -163,11 +164,13 @@ class YuthonProperty(models.Model): def get_domain(self, start_date=False, end_date=False, company_id=False): if not start_date or not end_date: return [] - if not company_id: + # 多选:company_id 标准化为列表 + cid_list = _id_list(company_id) + if not cid_list: return [] - # 这里进行条件的修改 - property_records = self.env['yuthon.property'].search([('company_id', '=', company_id)]) + # 按公司ID列表查询物业,获取片区管理员ID集合 + property_records = self.env['yuthon.property'].search([('company_id', 'in', cid_list)]) user_ids = set() for property_record in property_records: diff --git a/yuthon_report/models/rent_area_report.py b/yuthon_report/models/rent_area_report.py index 753198181..1155060db 100644 --- a/yuthon_report/models/rent_area_report.py +++ b/yuthon_report/models/rent_area_report.py @@ -2,6 +2,7 @@ import datetime import logging from odoo import api, models +from .report_utils import _id_list # partner_id 多选适配工具 _logger = logging.getLogger(__name__) @@ -33,8 +34,8 @@ class RentAreaReport(models.Model): keyword = params.get("keyword") condition = params.get('condition') - partner_id = params.get('partner_id') - company_id = params.get('company_id') + partner_id = _id_list(params.get('partner_id')) # 支持多选,转为列表 + company_id = _id_list(params.get('company_id')) # 前端侧边栏下拉选择器传入的 ID property_id = params.get('property_id') lessee_id = params.get('lessee_id') @@ -64,9 +65,9 @@ class RentAreaReport(models.Model): if condition: domain.append(('tenure_type', '=', condition)) if partner_id: - domain.append(('precinct_id', '=', partner_id)) + domain.append(('precinct_id', 'in', partner_id)) # 多选用 in if company_id: - domain.append(('company_id', '=', company_id)) + domain.append(('company_id', 'in', company_id)) # 物业名称下拉选择器:直接过滤 yuthon_property_id if property_id: domain.append(('yuthon_property_id', '=', int(property_id))) diff --git a/yuthon_report/models/report_utils.py b/yuthon_report/models/report_utils.py new file mode 100644 index 000000000..d2c2c420d --- /dev/null +++ b/yuthon_report/models/report_utils.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +"""报表公共工具函数""" +import logging + +_logger = logging.getLogger(__name__) + + +def _id_list(val): + """将参数标准化为扁平整数列表,用于多选场景下 'in' 操作符 + + - 列表/元组 → 递归展平后返回列表(防止 [[20, 30]] 嵌套导致 SQL 类型错误) + - 单个整数 → [val] + - None/False/0/空字符串 → [](空列表,使 if partner_id 为 False,不追加条件) + + 单选时 _id_list(20) → [20],配合 'in' [20] 等价于原来的 = 20,数据一致 + 多选时 _id_list([20, 30]) → [20, 30],配合 'in' [20, 30] 正常 + 嵌套时 _id_list([[20, 30]]) → [20, 30],防止 psycopg2 把 list 转为 ARRAY 导致 SQL 类型错误 + """ + if isinstance(val, (list, tuple)): + result = [] + for item in val: + # 递归展平嵌套的列表/元组 + if isinstance(item, (list, tuple)): + result.extend(_id_list(item)) + elif item is not None and item is not False and item != 0 and item != '': + result.append(item) + return result + if val is None or val is False or val == 0 or val == '': + return [] + return [val] diff --git a/yuthon_report/models/staff_position_report.py b/yuthon_report/models/staff_position_report.py index a2ed7705b..01716414b 100644 --- a/yuthon_report/models/staff_position_report.py +++ b/yuthon_report/models/staff_position_report.py @@ -1,3 +1,4 @@ +from .report_utils import _id_list from odoo import api, models from datetime import datetime from dateutil.relativedelta import relativedelta @@ -15,7 +16,7 @@ class StaffPositionReport(models.Model): # 不再过滤is_enable和establishment_quotas,查询所有岗位 domain = [] - company_id = params.get("company_id") + company_id = _id_list(params.get('company_id')) department_id = params.get("department_id") keyword = params.get("keyword") date_end = params.get("date_end") # 获取截止日期 diff --git a/yuthon_report/models/text_summary_report.py b/yuthon_report/models/text_summary_report.py index fe00f0664..4ad5f110e 100644 --- a/yuthon_report/models/text_summary_report.py +++ b/yuthon_report/models/text_summary_report.py @@ -2,6 +2,7 @@ import datetime import calendar +from .report_utils import _id_list from odoo import api, models @@ -31,7 +32,7 @@ class TextSummaryReport(models.Model): query_date = today # 获取筛选条件 - company_id = params.get('company_id') + company_id = _id_list(params.get('company_id')) precinct_id = params.get('partner_id') # 构建报表数据结构 diff --git a/yuthon_report/models/total_received_report.py b/yuthon_report/models/total_received_report.py index 38b9f38df..acaced3b5 100644 --- a/yuthon_report/models/total_received_report.py +++ b/yuthon_report/models/total_received_report.py @@ -2,6 +2,7 @@ import datetime import calendar from odoo import api, models +from .report_utils import _id_list class TotalReceivedReport(models.Model): @@ -38,8 +39,9 @@ class TotalReceivedReport(models.Model): ] condition = params.get('condition') - partner_id = params.get('partner_id') - company_id = params.get('company_id') + # 多选标准化:partner_id 统一为列表,配合 'in' 操作符 + partner_id = _id_list(params.get('partner_id')) + company_id = _id_list(params.get('company_id')) management_type_id = params.get('management_type_id') property_id = params.get('property_id') lessee_id = params.get('lessee_id') @@ -47,10 +49,11 @@ class TotalReceivedReport(models.Model): if condition: domain.append(('receivable_id.tenure_type', '=', condition)) + # 片区管理员:多选,用 'in' 操作符 if partner_id: - domain.append(('receivable_id.precinct_id', '=', partner_id)) + domain.append(('receivable_id.precinct_id', 'in', partner_id)) if company_id: - domain.append(('receivable_id.company_id', '=', company_id)) + domain.append(('receivable_id.company_id', 'in', company_id)) if management_type_id: domain.append(('receivable_id.management_type_id', '=', management_type_id)) if property_id: diff --git a/yuthon_report/models/yuthon_dashboard.py b/yuthon_report/models/yuthon_dashboard.py index 9601ba4de..3d47d8772 100644 --- a/yuthon_report/models/yuthon_dashboard.py +++ b/yuthon_report/models/yuthon_dashboard.py @@ -4,6 +4,7 @@ import calendar import logging from odoo import api, models +from .report_utils import _id_list _logger = logging.getLogger(__name__) @@ -150,6 +151,7 @@ class YuthonDashboard(models.Model): # ==================== 汇总信息 ==================== filters = { '统计时间': f"{query_date.year}-{query_date.month:02d}", + # 多选:所属公司传ID列表 '所属公司': self._get_company_display(params.get('company_id')), '管理员': self._get_admin_display(params.get('partner_id')), } @@ -157,10 +159,12 @@ class YuthonDashboard(models.Model): # ==================== 构建筛选条件上下文(透传给子报表) ==================== # 子报表从 action.context.dashboard_filter 中读取并初始化筛选状态 # date_str: "YYYY-MM" 格式,子报表据此还原 DateTime 对象 + # 多选:company_id/partner_id 透传ID列表,名称拼接 + cid_list = _id_list(params.get('company_id')) dashboard_filter = { 'date_str': f"{query_date.year}-{query_date.month:02d}" if has_date else '', - 'company_id': params.get('company_id') or False, - 'company_name': self._get_company_display(params.get('company_id')) if params.get('company_id') else '', + 'company_id': cid_list or False, + 'company_name': self._get_company_display(params.get('company_id')) if cid_list else '', 'partner_id': params.get('partner_id') or False, 'partner_name': self._get_admin_display(params.get('partner_id')) if params.get('partner_id') else '', 'condition': params.get('condition') or '', @@ -276,24 +280,40 @@ class YuthonDashboard(models.Model): # ------------------------------------------------------- def _get_company_display(self, company_id): + """根据公司ID列表获取公司显示名(多选支持) + company_id 是 yuthon.property.company 的 ID 列表(多选)或单值 + """ if not company_id: return '全部' try: - company = self.env['yuthon.property.company'].browse(int(company_id)) - return company.name if company else '未知' + # 多选:company_id 是列表,获取所有名称用逗号拼接 + cid_list = _id_list(company_id) + if not cid_list: + return '全部' + companies = self.env['yuthon.property.company'].browse(cid_list) + names = [c.name for c in companies if c.name] + return ', '.join(names) if names else '未知' except Exception: return '未知' def _get_admin_display(self, partner_id): - """根据片区ID获取管理员显示名 - partner_id 是 yuthon.precinct.message 的 ID(前端 Many2XAutocomplete 绑定该模型) + """根据片区ID列表获取管理员显示名(多选支持) + partner_id 是 yuthon.precinct.message 的 ID 列表(多选)或单值 display_name 已自动拼接为 "{片区名称}{管理员姓名}",如 "二B梁嘉辉" """ if not partner_id: return '全部' try: - precinct = self.env['yuthon.precinct.message'].browse(int(partner_id)) - return precinct.display_name if precinct else '未知' + # 多选:partner_id 是列表,获取所有名称用逗号拼接 + pid_list = _id_list(partner_id) + if not pid_list: + return '全部' + names = [] + for pid in pid_list: + precinct = self.env['yuthon.precinct.message'].browse(pid) + if precinct: + names.append(precinct.display_name) + return '、'.join(names) if names else '未知' except Exception: return '未知' @@ -304,8 +324,9 @@ class YuthonDashboard(models.Model): last_day = calendar.monthrange(query_date.year, query_date.month)[1] month_last = query_date.replace(day=last_day) - company_id = base_params.get('company_id') - partner_id = base_params.get('partner_id') + company_id = _id_list(base_params.get('company_id')) + # 多选标准化:partner_id 统一为列表 + partner_id = _id_list(base_params.get('partner_id')) keyword = base_params.get('keyword') # 按费用类型分别统计(数据源:property.bills.list) @@ -323,11 +344,12 @@ class YuthonDashboard(models.Model): ('actual_date', '<=', month_last), ] if company_id: - domain.append(('lessor_id', '=', int(company_id))) + domain.append(('lessor_id', 'in', company_id)) if partner_id: + # 多选:片区管理员用 'in' 操作符 domain += ['|', - ('contract_id.precinct_id', '=', int(partner_id)), - ('receivables_id.precinct_id', '=', int(partner_id)), + ('contract_id.precinct_id', 'in', partner_id), + ('receivables_id.precinct_id', 'in', partner_id), ] if keyword: domain += ['|', '|', '|', @@ -347,11 +369,12 @@ class YuthonDashboard(models.Model): ('actual_date', '<=', month_last), ] if company_id: - year_domain.append(('lessor_id', '=', int(company_id))) + year_domain.append(('lessor_id', 'in', company_id)) if partner_id: + # 多选:片区管理员用 'in' 操作符 year_domain += ['|', - ('contract_id.precinct_id', '=', int(partner_id)), - ('receivables_id.precinct_id', '=', int(partner_id)), + ('contract_id.precinct_id', 'in', partner_id), + ('receivables_id.precinct_id', 'in', partner_id), ] year_records = self.env['property.bills.list'].sudo().search(year_domain) year_total = sum((r.amount_actual or 0.0) for r in year_records) diff --git a/yuthon_report/static/src/components/yuthon_dashboard/yuthon_dashboard.js b/yuthon_report/static/src/components/yuthon_dashboard/yuthon_dashboard.js index 8b101fc11..2cf9991ed 100644 --- a/yuthon_report/static/src/components/yuthon_dashboard/yuthon_dashboard.js +++ b/yuthon_report/static/src/components/yuthon_dashboard/yuthon_dashboard.js @@ -5,6 +5,7 @@ import { Component, onMounted, useState } from "@odoo/owl"; import { useService } from "@web/core/utils/hooks"; import { standardActionServiceProps } from "@web/webclient/actions/action_service"; import { Many2XAutocomplete } from "@web/views/fields/relational_utils"; +import { MultiSelectAutocomplete } from "../yuthon_report/multi_select_autocomplete"; class YuthonDashboard extends Component { setup() { @@ -18,8 +19,10 @@ class YuthonDashboard extends Component { loading: false, collapsedSections: {}, // 筛选器 - selectCompany: {}, - selectPartner: {}, + // 多选:所属公司改为数组 + selectCompany: [], + // 多选:片区管理员改为数组 + selectPartner: [], condition: "", conditions: [ { name: "", value: "" }, @@ -37,12 +40,15 @@ class YuthonDashboard extends Component { const cached = sessionStorage.getItem("yuthon_dashboard_cache"); if (cached) { const snapshot = JSON.parse(cached); + // 从缓存恢复时,selectCompany/selectPartner 必须是数组, + // 旧格式({})或非数组值一律回退为空数组,防止 .map() 报错 + const safeArr = (val) => (Array.isArray(val) ? val : []); Object.assign(this.state, { dateStart: snapshot.dateStart || this.state.dateStart, - selectCompany: snapshot.selectCompany || this.state.selectCompany, - selectPartner: snapshot.selectPartner || this.state.selectPartner, + selectCompany: safeArr(snapshot.selectCompany), + selectPartner: safeArr(snapshot.selectPartner), condition: snapshot.condition || this.state.condition, - domainUsers: snapshot.domainUsers || this.state.domainUsers, + domainUsers: safeArr(snapshot.domainUsers), dashboardData: snapshot.dashboardData || this.state.dashboardData, collapsedSections: snapshot.collapsedSections || this.state.collapsedSections, }); @@ -86,8 +92,8 @@ class YuthonDashboard extends Component { } getDomain() { - const isCompanyEmpty = Object.keys(this.state.selectCompany).length === 0; - if (isCompanyEmpty) { + // 多选:检查公司数组是否为空 + if (!this.state.selectCompany.length) { return []; } else { return [["id", "in", this.state.domainUsers]]; @@ -95,7 +101,8 @@ class YuthonDashboard extends Component { } async searchUserDomain() { - this.state.selectPartner = {}; + // 多选:重置片区管理员为空数组 + this.state.selectPartner = []; // get_domain 需要 start_date 和 end_date,否则直接返回 [] // 从当前选择的月份推算,没选月份就用当月 @@ -115,6 +122,8 @@ class YuthonDashboard extends Component { endDate = `${y}-${m}-${String(lastDay).padStart(2, "0")}`; } + // 多选:传公司ID列表 + const companyIdList = this.state.selectCompany.map(item => item.id); const results = await this.serviceOrm.call( "property.accounts.receivable", "get_domain", @@ -122,33 +131,28 @@ class YuthonDashboard extends Component { { start_date: startDate, end_date: endDate, - company_id: this.state.selectCompany?.id, + company_id: companyIdList, } ); this.state.domainUsers = results || []; } - async onUpdateCompany(v) { - if (!v || !v[0]) { - this.state.selectCompany = {}; - this.state.selectPartner = {}; - this.state.domainUsers = []; - await this.onSearch(); - return; + async onUpdateCompany(items) { + // 多选:items 是已选项数组 [{id, display_name}, ...] + this.state.selectCompany = items; + // 多选:片区管理员重置为空数组 + this.state.selectPartner = []; + this.state.domainUsers = []; + // 有公司选中时才查询domain + if (items.length) { + await this.searchUserDomain(); } - const values = await this.serviceOrm.call( - "yuthon.property.company", - "read", - [[v[0].id]], - { fields: ["display_name"] } - ); - this.state.selectCompany = values[0] || {}; - await this.searchUserDomain(); await this.onSearch(); } - async onUpdatePartner(v) { - this.state.selectPartner = v[0] || {}; + async onUpdatePartner(items) { + // 多选:items 是已选项数组 [{id, display_name}, ...] + this.state.selectPartner = items; await this.onSearch(); } @@ -164,8 +168,10 @@ class YuthonDashboard extends Component { const params = { date_start: this.state.dateStart, keyword: this.state.keyword, - company_id: this.state.selectCompany?.id || false, - partner_id: this.state.selectPartner?.id || false, + // 多选:所属公司传ID列表 + company_id: this.state.selectCompany.map(item => item.id), + // 多选:片区管理员传ID列表 + partner_id: this.state.selectPartner.map(item => item.id), condition: this.state.condition || false, }; @@ -205,10 +211,12 @@ class YuthonDashboard extends Component { // 构建当前看板的筛选参数,透传给子报表 const dashboardFilter = { date_str: this.state.dateStart || "", // "YYYY-MM" - company_id: this.state.selectCompany?.id || false, - company_name: this.state.selectCompany?.display_name || "", - partner_id: this.state.selectPartner?.id || false, - partner_name: this.state.selectPartner?.display_name || "", + // 多选:所属公司传ID列表和名称拼接 + company_id: this.state.selectCompany.map(item => item.id), + company_name: this.state.selectCompany.map(item => item.display_name).join(", "), + // 多选:片区管理员传ID列表和名称拼接 + partner_id: this.state.selectPartner.map(item => item.id), + partner_name: this.state.selectPartner.map(item => item.display_name).join(", "), condition: this.state.condition || "", }; @@ -247,6 +255,7 @@ YuthonDashboard.props = { ...standardActionServiceProps }; YuthonDashboard.template = "yuthon_dashboard.YuthonDashboard"; YuthonDashboard.components = { Many2XAutocomplete, + MultiSelectAutocomplete, }; registry.category("actions").add("yuthon_dashboard_client", YuthonDashboard); diff --git a/yuthon_report/static/src/components/yuthon_dashboard/yuthon_dashboard.xml b/yuthon_report/static/src/components/yuthon_dashboard/yuthon_dashboard.xml index 5858b47c2..d0af935b0 100644 --- a/yuthon_report/static/src/components/yuthon_dashboard/yuthon_dashboard.xml +++ b/yuthon_report/static/src/components/yuthon_dashboard/yuthon_dashboard.xml @@ -18,14 +18,15 @@
- +
@@ -43,14 +44,15 @@
- +
diff --git a/yuthon_report/static/src/components/yuthon_report/att_report.js b/yuthon_report/static/src/components/yuthon_report/att_report.js index 1e0f1ad6f..a07304e20 100644 --- a/yuthon_report/static/src/components/yuthon_report/att_report.js +++ b/yuthon_report/static/src/components/yuthon_report/att_report.js @@ -10,6 +10,7 @@ import { import {DateTimeInput} from "@web/core/datetime/datetime_input"; import {CheckBox} from "@web/core/checkbox/checkbox"; import {Many2XAutocomplete} from "@web/views/fields/relational_utils"; +import {MultiSelectAutocomplete} from "./multi_select_autocomplete"; import {formatDate} from "@web/core/l10n/dates" const {DateTime} = luxon; @@ -27,7 +28,8 @@ class ReceivableReport extends Component { keyword: "", booleanValue: true, selectDepartment: {}, - selectCompany: {}, + // 多选:所属公司改为数组 + selectCompany: [], selectStructure: {}, domainDepartment:[], domainStructure:[], @@ -101,8 +103,8 @@ class ReceivableReport extends Component { getDomainStructure() { console.log(this.state) - const isCompanyEmpty = Object.keys(this.state.selectCompany).length === 0; - if (isCompanyEmpty) { + // 多选:检查公司数组是否为空 + if (!this.state.selectCompany.length) { return []; } else { return [['id','in', this.state.domainStructure]]; @@ -112,8 +114,8 @@ class ReceivableReport extends Component { getDomainDepartment() { console.log(this.state) - const isCompanyEmpty = Object.keys(this.state.selectCompany).length === 0; - if (isCompanyEmpty) { + // 多选:检查公司数组是否为空 + if (!this.state.selectCompany.length) { return []; } else { return [['id','in', this.state.domainDepartment]]; @@ -121,12 +123,14 @@ class ReceivableReport extends Component { } async searchDomain() { + // 多选:传公司ID列表 + const companyIdList = this.state.selectCompany.map(item => item.id) let results = await this.serviceOrm.call( 'hr.attendance', 'get_domain', [], { - company_id: this.state.selectCompany?.id, + company_id: companyIdList, } ) console.log(results) @@ -143,7 +147,8 @@ class ReceivableReport extends Component { date_start: `${startDate} 00:00:00`, date_end: `${endDate} 23:59:59`, department_id: this.state.selectDepartment?.id, - company_id: this.state.selectCompany?.id, + // 多选:所属公司传ID列表 + company_id: this.state.selectCompany.map(item => item.id), structure_id: this.state.selectStructure?.id, }; const result = await this.serviceOrm.call( @@ -169,9 +174,12 @@ class ReceivableReport extends Component { }); } - async onUpdateCompany(v) { - this.state.selectCompany = v[0] || {} - await this.searchDomain() + async onUpdateCompany(items) { + // 多选:items 是已选项数组 + this.state.selectCompany = items + if (items.length) { + await this.searchDomain() + } // 自动搜索 await this.onSearch() } @@ -211,6 +219,7 @@ ReceivableReport.components = { DateTimeInput, CheckBox, Many2XAutocomplete, + MultiSelectAutocomplete, }; registry.category("actions").add("att_report_client", ReceivableReport); diff --git a/yuthon_report/static/src/components/yuthon_report/att_report.xml b/yuthon_report/static/src/components/yuthon_report/att_report.xml index e482c9148..6d88e278e 100644 --- a/yuthon_report/static/src/components/yuthon_report/att_report.xml +++ b/yuthon_report/static/src/components/yuthon_report/att_report.xml @@ -36,16 +36,16 @@ - +
-
diff --git a/yuthon_report/static/src/components/yuthon_report/multi_select_autocomplete.js b/yuthon_report/static/src/components/yuthon_report/multi_select_autocomplete.js new file mode 100644 index 000000000..a57dce940 --- /dev/null +++ b/yuthon_report/static/src/components/yuthon_report/multi_select_autocomplete.js @@ -0,0 +1,220 @@ +/** @odoo-module **/ + +/** + * 多选自动完成组件(MultiSelectAutocomplete) + * 用于报表搜索栏,支持选择多个记录,以标签(chip)形式显示已选项 + * 基于 Odoo 的 name_search API 实现搜索 + */ +import { Component, onMounted, useRef, useState } from "@odoo/owl"; +import { useService } from "@web/core/utils/hooks"; + +export class MultiSelectAutocomplete extends Component { + setup() { + this.orm = useService("orm"); + this.inputRef = useRef("input"); + this.containerRef = useRef("container"); + + // 响应式状态 + this.state = useState({ + searchText: "", // 输入框中的搜索文字 + options: [], // 下拉候选列表 + showDropdown: false, // 是否显示下拉框 + isLoading: false, // 是否正在加载 + }); + + // 绑定外部点击关闭事件 + this._boundCloseOutside = this._closeOnOutside.bind(this); + } + + mounted() { + document.addEventListener("click", this._boundCloseOutside); + } + + willUnmount() { + document.removeEventListener("click", this._boundCloseOutside); + } + + /** + * 点击外部区域时关闭下拉框 + */ + _closeOnOutside(ev) { + if (this.containerRef.el && !this.containerRef.el.contains(ev.target)) { + this.state.showDropdown = false; + } + } + + /** 已选项列表(从 props.value 获取) */ + get selectedItems() { + return this.props.value || []; + } + + /** 获取已选ID列表(用于排除已选的) */ + getSelectedIds() { + return this.selectedItems.map((item) => item.id); + } + + /** + * 调用 Odoo 的 name_search 搜索候选记录 + */ + async _searchRecords(name) { + const limit = this.props.searchLimit || 20; + let domain = []; + // getDomain 是可选的,没有传则用空 domain(不过滤) + if (this.props.getDomain) { + try { + domain = this.props.getDomain(); + } catch (e) { + console.error("[MultiSelect] getDomain error:", e); + domain = []; + } + } + // 排除已选中的记录 + const excludeIds = this.getSelectedIds(); + if (excludeIds.length > 0) { + domain.push(["id", "not in", excludeIds]); + } + + // 调用后端 name_search 接口获取候选列表 + const results = await this.orm.call( + this.props.resModel, + "name_search", + [], + { + name: name || "", + operator: "ilike", + args: domain, + limit: limit, + context: this.props.context || {}, + } + ); + // 将 [id, display_name] 格式转为对象数组 + return results.map((r) => ({ + id: r[0], + display_name: r[1] ? r[1].split("\n")[0] : "未命名", + })); + } + + /** + * 输入框获得焦点时:立即打开下拉并开始搜索 + */ + async onInputFocus() { + // 立即显示下拉框(先显示加载中,数据到了再更新) + this.state.showDropdown = true; + await this._doSearch(); + } + + /** + * 输入框失焦时:延迟关闭下拉(让点击候选项的事件先执行) + */ + onInputBlur() { + // 延迟关闭,避免点击候选项时先 blur 关闭了下拉,导致点击失效 + setTimeout(() => { + this.state.showDropdown = false; + }, 150); + } + + /** + * 输入内容变化时:重新搜索 + */ + async onInputChange(ev) { + // 从 DOM 直接读取输入值(避免 t-att-value 重置问题) + this.state.searchText = ev.target.value; + this.state.showDropdown = true; + await this._doSearch(); + } + + /** + * 执行搜索:调用后端接口更新候选项 + */ + async _doSearch() { + this.state.isLoading = true; + try { + const options = await this._searchRecords(this.state.searchText); + this.state.options = options; + } catch (e) { + console.error("[MultiSelect] search error:", e); + this.state.options = []; + } finally { + this.state.isLoading = false; + } + } + + /** + * 点击下拉候选项:添加到已选列表 + */ + onSelect(option) { + const newItem = { id: option.id, display_name: option.display_name }; + const currentItems = [...this.selectedItems]; + // 防止重复添加 + if (!currentItems.find((item) => item.id === newItem.id)) { + currentItems.push(newItem); + } + // 通知父组件更新 + this.props.update(currentItems); + // 清空搜索文字,关闭下拉 + this.state.searchText = ""; + this.state.showDropdown = false; + // 选中后触发搜索回调(如报表查询) + if (this.props.searchOnChange !== false && this.props.onSearch) { + this.props.onSearch(); + } + } + + /** + * 点击标签删除按钮:移除一个已选项 + */ + onRemoveItem(id) { + const currentItems = this.selectedItems.filter((item) => item.id !== id); + this.props.update([...currentItems]); + if (this.props.searchOnChange !== false && this.props.onSearch) { + this.props.onSearch(); + } + } + + /** + * 点击清空按钮:清空所有已选项 + */ + clearAll() { + this.props.update([]); + if (this.props.searchOnChange !== false && this.props.onSearch) { + this.props.onSearch(); + } + } + + /** + * 键盘事件:Backspace 删除最后一项,Enter 阻止默认行为 + */ + onKeyDown(ev) { + if (ev.key === "Backspace" && !this.state.searchText && this.selectedItems.length > 0) { + // 删除键且输入为空时,移除最后一个已选项 + const lastItem = this.selectedItems[this.selectedItems.length - 1]; + this.onRemoveItem(lastItem.id); + } else if (ev.key === "Enter") { + // 回车时阻止表单提交 + ev.preventDefault(); + } + } +} + +// 注册模板和属性 +MultiSelectAutocomplete.template = "yuthon_report.MultiSelectAutocomplete"; +MultiSelectAutocomplete.props = { + value: { type: Array, optional: true }, + resModel: String, + getDomain: { type: Function, optional: true }, + update: Function, + placeholder: { type: String, optional: true }, + fieldString: { type: String, optional: true }, + context: { type: Object, optional: true }, + searchLimit: { type: Number, optional: true }, + /** 选中/删除后是否自动调用 onSearch 回调,默认 true */ + searchOnChange: { type: Boolean, optional: true }, + /** 选中/删除后的回调(如触发报表查询) */ + onSearch: { type: Function, optional: true }, +}; +MultiSelectAutocomplete.defaultProps = { + value: [], + searchLimit: 20, + searchOnChange: true, + context: {}, +}; diff --git a/yuthon_report/static/src/components/yuthon_report/multi_select_autocomplete.xml b/yuthon_report/static/src/components/yuthon_report/multi_select_autocomplete.xml new file mode 100644 index 000000000..c4efb76b2 --- /dev/null +++ b/yuthon_report/static/src/components/yuthon_report/multi_select_autocomplete.xml @@ -0,0 +1,72 @@ + + + + +
+ + +
+ + + + + + + + + + + +
+ + + +
+
+
diff --git a/yuthon_report/static/src/components/yuthon_report/receivable_flow_report.js b/yuthon_report/static/src/components/yuthon_report/receivable_flow_report.js index 580441374..67d8d1c3b 100644 --- a/yuthon_report/static/src/components/yuthon_report/receivable_flow_report.js +++ b/yuthon_report/static/src/components/yuthon_report/receivable_flow_report.js @@ -10,6 +10,7 @@ import { import {DateTimeInput} from "@web/core/datetime/datetime_input"; import {CheckBox} from "@web/core/checkbox/checkbox"; import {Many2XAutocomplete} from "@web/views/fields/relational_utils"; +import {MultiSelectAutocomplete} from "./multi_select_autocomplete"; import {formatDate} from "@web/core/l10n/dates" const {DateTime} = luxon; @@ -30,8 +31,9 @@ class ReceivableFlowReport extends Component { singleDate: DateTime.now().startOf("month"), keyword: "", booleanValue: true, - selectPartner: {}, - selectCompany: {}, + // 多选:片区管理员和所属公司改为数组 + selectPartner: [], + selectCompany: [], selectManagementType: {}, selectProperty: {}, selectLessee: {}, @@ -287,30 +289,33 @@ class ReceivableFlowReport extends Component { } getDomain() { - const isCompanyEmpty = Object.keys(this.state.selectCompany).length === 0; - if (isCompanyEmpty) { + // 多选:检查公司数组是否为空 + if (!this.state.selectCompany.length) { return []; } else { return [['id', 'in', this.state.domainUsers]]; } } - async onUpdatePartner(v) { - this.state.selectPartner = v[0] + async onUpdatePartner(items) { + // 多选:items 是已选项数组 + this.state.selectPartner = items await this.onSearch() } async searchUserDomain() { - this.state.selectPartner = {} + this.state.selectPartner = [] let startDate = formatDate(this.state.startDate, {format: "yyyy-MM-dd"}) let endDate = formatDate(this.state.endDate, {format: "yyyy-MM-dd"}) + // 多选:传公司ID列表 + const companyIdList = this.state.selectCompany.map(item => item.id) let results = await this.serviceOrm.call( 'property.accounts.receivable', 'get_domain', [], { - company_id: this.state.selectCompany?.id, + company_id: companyIdList, start_date: startDate, end_date: endDate, } @@ -318,24 +323,14 @@ class ReceivableFlowReport extends Component { this.state.domainUsers = results } - async onUpdateCompany(v) { - if (!v || !v[0]) { - this.state.selectCompany = {}; - this.state.selectPartner = {}; - this.state.domainUsers = []; - await this.onSearch(); - return; + async onUpdateCompany(items) { + // 多选:items 是已选项数组 + this.state.selectCompany = items + this.state.selectPartner = []; + this.state.domainUsers = []; + if (items.length) { + await this.searchUserDomain() } - let values = await this.serviceOrm.call( - 'yuthon.property.company', - 'read', - [[v[0].id]], - { - fields: ['display_name'], - } - ) - this.state.selectCompany = values[0] || {} - await this.searchUserDomain() await this.onSearch() } @@ -369,8 +364,9 @@ class ReceivableFlowReport extends Component { date_end: `${singleDate} 23:59:59`, keyword: this.state.keyword || false, condition: this.state.condition || false, - partner_id: this.state.selectPartner?.id, - company_id: this.state.selectCompany?.id, + // 多选:片区管理员、所属公司传ID列表 + partner_id: this.state.selectPartner.map(item => item.id), + company_id: this.state.selectCompany.map(item => item.id), management_type_id: this.state.selectManagementType?.id || false, property_id: this.state.selectProperty?.id || false, lessee_id: this.state.selectLessee?.id || false, @@ -384,8 +380,9 @@ class ReceivableFlowReport extends Component { date_end: `${endDate} 23:59:59`, keyword: this.state.keyword || false, condition: this.state.condition || false, - partner_id: this.state.selectPartner?.id, - company_id: this.state.selectCompany?.id, + // 多选:片区管理员、所属公司传ID列表 + partner_id: this.state.selectPartner.map(item => item.id), + company_id: this.state.selectCompany.map(item => item.id), management_type_id: this.state.selectManagementType?.id || false, property_id: this.state.selectProperty?.id || false, lessee_id: this.state.selectLessee?.id || false, @@ -552,6 +549,7 @@ ReceivableFlowReport.components = { DateTimeInput, CheckBox, Many2XAutocomplete, + MultiSelectAutocomplete, }; registry.category("actions").add("receivable_flow_report_client", ReceivableFlowReport); diff --git a/yuthon_report/static/src/components/yuthon_report/receivable_message.xml b/yuthon_report/static/src/components/yuthon_report/receivable_message.xml index 848d9bb80..548d61db2 100644 --- a/yuthon_report/static/src/components/yuthon_report/receivable_message.xml +++ b/yuthon_report/static/src/components/yuthon_report/receivable_message.xml @@ -36,29 +36,31 @@ - +
-
- +
-
diff --git a/yuthon_report/static/src/components/yuthon_report/receivable_message_report.js b/yuthon_report/static/src/components/yuthon_report/receivable_message_report.js index 9b8bbb690..1ca2ede7d 100644 --- a/yuthon_report/static/src/components/yuthon_report/receivable_message_report.js +++ b/yuthon_report/static/src/components/yuthon_report/receivable_message_report.js @@ -6,6 +6,7 @@ import {useService} from "@web/core/utils/hooks"; import {standardActionServiceProps} from "@web/webclient/actions/action_service"; import {DateTimeInput} from "@web/core/datetime/datetime_input"; import {Many2XAutocomplete} from "@web/views/fields/relational_utils"; +import {MultiSelectAutocomplete} from "./multi_select_autocomplete"; import {formatDate} from "@web/core/l10n/dates" const {DateTime} = luxon; @@ -19,8 +20,9 @@ class ReceiptLedgerReport extends Component { this.state = useState({ startDate: null, endDate: null, - selectPrecinct: {}, - selectCompany: {}, + // 多选:片区管理员和所属公司改为数组 + selectPrecinct: [], + selectCompany: [], selectManagementType: {}, conditions: [{ 'name': '自有', @@ -74,15 +76,16 @@ class ReceiptLedgerReport extends Component { return []; } - // 选择所属片区 - async onUpdatePrecinct(v) { - console.log("选择所属片区:", v); - this.state.selectPrecinct = v[0] || {}; + // 选择所属片区(多选) + async onUpdatePrecinct(items) { + console.log("选择所属片区:", items); + this.state.selectPrecinct = items; } - async onUpdateCompany(v) { - console.log("选择所属公司:", v); - this.state.selectCompany = v[0] || {}; + // 选择所属公司(多选) + async onUpdateCompany(items) { + console.log("选择所属公司:", items); + this.state.selectCompany = items; } async onUpdateManagementType(v) { @@ -109,10 +112,11 @@ class ReceiptLedgerReport extends Component { const params = { date_start: this.state.startDate, date_end: this.state.endDate, - precinct_id: this.state.selectPrecinct?.id || false, + // 多选:片区管理员和所属公司传ID列表 + precinct_id: this.state.selectPrecinct.map(item => item.id), condition: this.state.condition || false, - company_id: this.state.selectCompany?.id || false, + company_id: this.state.selectCompany.map(item => item.id), management_type_id: this.state.selectManagementType?.id || false, }; @@ -142,6 +146,7 @@ ReceiptLedgerReport.template = "yuthon_report.ReceiptLedgerReport"; ReceiptLedgerReport.components = { DateTimeInput, Many2XAutocomplete, + MultiSelectAutocomplete, }; registry.category("actions").add("receipt_ledger_report_client", ReceiptLedgerReport); 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 0f87ffe30..725daae3d 100644 --- a/yuthon_report/static/src/components/yuthon_report/receivable_report.js +++ b/yuthon_report/static/src/components/yuthon_report/receivable_report.js @@ -10,6 +10,7 @@ import { import {DateTimeInput} from "@web/core/datetime/datetime_input"; import {CheckBox} from "@web/core/checkbox/checkbox"; import {Many2XAutocomplete} from "@web/views/fields/relational_utils"; +import {MultiSelectAutocomplete} from "./multi_select_autocomplete"; import {formatDate} from "@web/core/l10n/dates" const {DateTime} = luxon; @@ -40,14 +41,32 @@ class ReceivableReport extends Component { initSingleDate = parsed.startOf("month"); } } - // 解析所属公司:有 id+name 才初始化,否则为空 - const initCompany = df.company_id - ? { id: df.company_id, display_name: df.company_name || '' } - : {}; - // 解析片区管理员 - const initPartner = df.partner_id - ? { id: df.partner_id, display_name: df.partner_name || '' } - : {}; + // 解析所属公司(多选:从看板跳转时 company_id 可能是数组 [1, 2] 或单值 1) + let initCompany = []; + if (df.company_id) { + // company_name 可能是逗号拼接的多个名称 + const nameList = (df.company_name || '').split(/[,,、]/).map(s => s.trim()).filter(Boolean); + // 统一转为数组 + const cidArr = Array.isArray(df.company_id) ? df.company_id : [df.company_id]; + // 每个 ID 创建一个选项对象 + initCompany = cidArr.map((cid, idx) => ({ + id: cid, + display_name: nameList[idx] || '', + })); + } + // 解析片区管理员(多选:从看板跳转时 partner_id 可能是数组 [20, 30] 或单值 20) + let initPartner = []; + if (df.partner_id) { + // partner_name 可能是逗号拼接的多个名称 + const nameList = (df.partner_name || '').split(/[,,、]/).map(s => s.trim()).filter(Boolean); + // 统一转为数组 + const pidArr = Array.isArray(df.partner_id) ? df.partner_id : [df.partner_id]; + // 每个 ID 创建一个选项对象 + initPartner = pidArr.map((pid, idx) => ({ + id: pid, + display_name: nameList[idx] || '', + })); + } // 解析权属类型 const initCondition = df.condition || false; @@ -313,8 +332,8 @@ class ReceivableReport extends Component { getDomain() { console.log(this.state) - const isCompanyEmpty = Object.keys(this.state.selectCompany).length === 0; - if (isCompanyEmpty) { + // 多选:检查公司数组是否为空 + if (!this.state.selectCompany.length) { return []; } else { return [['id', 'in', this.state.domainUsers]]; @@ -322,24 +341,27 @@ class ReceivableReport extends Component { } - async onUpdatePartner(v) { - console.log(v) - this.state.selectPartner = v[0] + async onUpdatePartner(items) { + // 多选:items 是已选项数组 [{id, display_name}, ...] + this.state.selectPartner = items // 自动搜索 await this.onSearch() } async searchUserDomain() { - this.state.selectPartner = {} + // 多选:重置片区管理员为空数组 + this.state.selectPartner = [] let startDate = formatDate(this.state.startDate, {format: "yyyy-MM-dd"}) let endDate = formatDate(this.state.endDate, {format: "yyyy-MM-dd"}) + // 多选:传公司ID列表 + const companyIdList = this.state.selectCompany.map(item => item.id) let results = await this.serviceOrm.call( 'property.accounts.receivable', 'get_domain', [], { - company_id: this.state.selectCompany?.id, + company_id: companyIdList, start_date: startDate, end_date: endDate, } @@ -349,32 +371,16 @@ class ReceivableReport extends Component { } - async onUpdateCompany(v) { - console.log(v) - // 检查v是否为空或v[0]不存在 - if (!v || !v[0]) { - this.state.selectCompany = {}; - this.state.selectPartner = {}; - this.state.domainUsers = []; - await this.onSearch(); - return; + async onUpdateCompany(items) { + // 多选:items 是已选项数组 [{id, display_name}, ...] + this.state.selectCompany = items + // 多选:片区管理员重置为空数组 + this.state.selectPartner = []; + this.state.domainUsers = []; + // 有公司选中时才查询domain + if (items.length) { + await this.searchUserDomain() } - // 执行一次 查询 - // 通过 read 重新查询一次, 更多只返回了 id - let values = await this.serviceOrm.call( - 'yuthon.property.company', - 'read', - [[v[0].id]], - { - fields: ['display_name'], - } - ) - console.log(values) - this.state.selectCompany = values[0] || {} - // 查询这个过程中的 user_id 组合成列表.返回. - // 重置 partner 筛选结果 - // 修改过滤条件 - await this.searchUserDomain() // 自动搜索 await this.onSearch() } @@ -413,8 +419,10 @@ class ReceivableReport extends Component { date_end: `${singleDate} 23:59:59`, keyword: this.state.keyword || false, condition: this.state.condition || false, - partner_id: this.state.selectPartner?.id, - company_id: this.state.selectCompany?.id, + // 多选:片区管理员传ID列表 + partner_id: this.state.selectPartner.map(item => item.id), + // 多选:所属公司传ID列表 + company_id: this.state.selectCompany.map(item => item.id), management_type_id: this.state.selectManagementType?.id || false, property_id: this.state.selectProperty?.id || false, lessee_id: this.state.selectLessee?.id || false, @@ -428,8 +436,10 @@ class ReceivableReport extends Component { date_end: `${endDate} 23:59:59`, keyword: this.state.keyword || false, condition: this.state.condition || false, - partner_id: this.state.selectPartner?.id, - company_id: this.state.selectCompany?.id, + // 多选:片区管理员传ID列表 + partner_id: this.state.selectPartner.map(item => item.id), + // 多选:所属公司传ID列表 + company_id: this.state.selectCompany.map(item => item.id), management_type_id: this.state.selectManagementType?.id || false, property_id: this.state.selectProperty?.id || false, lessee_id: this.state.selectLessee?.id || false, @@ -619,6 +629,7 @@ ReceivableReport.components = { DateTimeInput, CheckBox, Many2XAutocomplete, + MultiSelectAutocomplete, }; registry.category("actions").add("receivable_report_client", ReceivableReport); diff --git a/yuthon_report/static/src/components/yuthon_report/receivable_report.xml b/yuthon_report/static/src/components/yuthon_report/receivable_report.xml index 53835b1b7..5b858d5bc 100644 --- a/yuthon_report/static/src/components/yuthon_report/receivable_report.xml +++ b/yuthon_report/static/src/components/yuthon_report/receivable_report.xml @@ -63,16 +63,16 @@ - +
-
@@ -86,16 +86,16 @@
- +
-
diff --git a/yuthon_report/static/src/components/yuthon_report/yuthon_report.js b/yuthon_report/static/src/components/yuthon_report/yuthon_report.js index 26cbefada..62b0bf8df 100644 --- a/yuthon_report/static/src/components/yuthon_report/yuthon_report.js +++ b/yuthon_report/static/src/components/yuthon_report/yuthon_report.js @@ -8,6 +8,7 @@ import { standardActionServiceProps } from "@web/webclient/actions/action_servic import { DateTimeInput } from "@web/core/datetime/datetime_input"; import { CheckBox } from "@web/core/checkbox/checkbox"; import { Many2XAutocomplete } from "@web/views/fields/relational_utils"; +import { MultiSelectAutocomplete } from "./multi_select_autocomplete"; import {formatDate} from "@web/core/l10n/dates" const {DateTime} = luxon; @@ -34,12 +35,26 @@ class YouthonReport extends Component { initEndDate = parsed.endOf("month"); } } - const initCompany = df.company_id - ? { id: df.company_id, display_name: df.company_name || '' } - : {}; - const initPartner = df.partner_id - ? { id: df.partner_id, display_name: df.partner_name || '' } - : {}; + // 多选:解析所属公司 + let initCompany = []; + if (df.company_id) { + const nameList = (df.company_name || '').split(/[,,、]/).map(s => s.trim()).filter(Boolean); + const cidArr = Array.isArray(df.company_id) ? df.company_id : [df.company_id]; + initCompany = cidArr.map((cid, idx) => ({ + id: cid, + display_name: nameList[idx] || '', + })); + } + // 多选:解析片区管理员 + let initPartner = []; + if (df.partner_id) { + const nameList = (df.partner_name || '').split(/[,,、]/).map(s => s.trim()).filter(Boolean); + const pidArr = Array.isArray(df.partner_id) ? df.partner_id : [df.partner_id]; + initPartner = pidArr.map((pid, idx) => ({ + id: pid, + display_name: nameList[idx] || '', + })); + } const initCondition = df.condition || false; // 是否从经营看板跳转过来(控制"返回看板"按钮显示) @@ -53,8 +68,7 @@ class YouthonReport extends Component { endDate: initEndDate, keyword: "", selectPartner: initPartner, - selectCompany: initCompany, - condition: initCondition, + selectCompany: initCompany, condition: initCondition, conditions:[{ 'name':'自有', 'value':'self' @@ -104,13 +118,14 @@ class YouthonReport extends Component { // 过滤条件 return [['id', 'in', [107,106, 215,112,110,214,218,173,219]]] } - async onUpdatePartner(v){ - console.log(v) - this.state.selectPartner = v[0] + async onUpdatePartner(items){ + // 多选:items 是已选项数组 + this.state.selectPartner = items } - async onUpdateCompany(v){ - this.state.selectCompany = v[0] + async onUpdateCompany(items){ + // 多选:items 是已选项数组 + this.state.selectCompany = items } async onSearch() { @@ -130,8 +145,9 @@ class YouthonReport extends Component { date_end: endStr ? `${endStr} 23:59:59` : false, keyword: this.state.keyword || false, condition:this.state.condition || false, - partner_id:this.state.selectPartner?.id, - company_id:this.state.selectCompany?.id , + // 多选:片区管理员、所属公司传ID列表 + partner_id: this.state.selectPartner.map(item => item.id), + company_id: this.state.selectCompany.map(item => item.id), }; const result = await this.serviceOrm.call( this.config.model, @@ -178,6 +194,7 @@ YouthonReport.components = { DateTimeInput, CheckBox, Many2XAutocomplete, + MultiSelectAutocomplete, }; registry.category("actions").add("youthon_report_client", YouthonReport); diff --git a/yuthon_report/static/src/components/yuthon_report/yuthon_report.xml b/yuthon_report/static/src/components/yuthon_report/yuthon_report.xml index 32fb92708..acd8435f2 100644 --- a/yuthon_report/static/src/components/yuthon_report/yuthon_report.xml +++ b/yuthon_report/static/src/components/yuthon_report/yuthon_report.xml @@ -87,16 +87,17 @@ - +
-
@@ -108,16 +109,17 @@ -
+
-