diff --git a/yuthon_report/models/cumulative_arrears_report.py b/yuthon_report/models/cumulative_arrears_report.py index 94b7e7715..e9da491e6 100644 --- a/yuthon_report/models/cumulative_arrears_report.py +++ b/yuthon_report/models/cumulative_arrears_report.py @@ -47,15 +47,21 @@ class CumulativeArrearsReport(models.Model): if management_type_id: domain.append(('management_type_id', '=', management_type_id)) - receivables = self.search(domain, limit=3000, order="due_date asc") + receivables = self.search(domain, order="due_date asc") rows = [] + type_totals = {} total_arrears = 0.0 for idx, rec in enumerate(receivables, start=1): contract = rec.contract_id rent = contract.rent or rec.original_amount or 0.0 arrears = rec.submitted or 0.0 total_arrears += arrears + + type_name = rec.management_type_id.name if rec.management_type_id else "未分类" + if type_name not in type_totals: + type_totals[type_name] = {'arrears': 0.0} + type_totals[type_name]['arrears'] += arrears rows.append({ "序号": idx, @@ -70,19 +76,10 @@ class CumulativeArrearsReport(models.Model): "管理员": rec.admin_id.name if rec.admin_id else "", }) - return { - "rows": rows, - "summary": { - "序号": False, - "物业名称": False, - "面积": False, - "承租方名称": False, - "合同编号": False, - "月租金": False, - "欠租金额": round(total_arrears, 2), - "欠租所属月份": False, - "所属公司": False, - "管理员": False, - } - } + summary_stats = [] + for type_name in sorted(type_totals.keys()): + summary_stats.append({'name': type_name, 'amount': round(type_totals[type_name]['arrears'], 2)}) + summary_stats.append({'name': '总合计', 'amount': round(total_arrears, 2)}) + + return {"rows": rows, "stats": summary_stats} diff --git a/yuthon_report/models/current_receivable_property_fee_report.py b/yuthon_report/models/current_receivable_property_fee_report.py index 374fcd0b8..40f8f2dcf 100644 --- a/yuthon_report/models/current_receivable_property_fee_report.py +++ b/yuthon_report/models/current_receivable_property_fee_report.py @@ -66,11 +66,13 @@ class CurrentReceivablePropertyFeeReport(models.Model): type_domain = domain[:] # 先按类型+日期,再放宽日期,再放宽类型 - receivables = self.search(type_domain, limit=3000, order="due_date asc") + receivables = self.search(type_domain, order="due_date asc") if not receivables and type_domain != domain: - receivables = self.search(domain, limit=3000, order="due_date asc") + receivables = self.search(domain, order="due_date asc") rows = [] + # 按管理类型分组统计 + type_totals = {} # {'管理类型名称': {'amount': 0, 'amount_nt': 0}} total_amount = 0.0 total_amount_nt = 0.0 @@ -80,6 +82,13 @@ class CurrentReceivablePropertyFeeReport(models.Model): amt_nt = amt / (1 + tax_rate / 100) if tax_rate else amt total_amount += amt total_amount_nt += amt_nt + + # 统计每个管理类型的合计 + type_name = rec.management_type_id.name if rec.management_type_id else "未分类" + if type_name not in type_totals: + type_totals[type_name] = {'amount': 0.0, 'amount_nt': 0.0} + type_totals[type_name]['amount'] += amt + type_totals[type_name]['amount_nt'] += amt_nt rows.append({ "序号": idx, @@ -94,19 +103,18 @@ class CurrentReceivablePropertyFeeReport(models.Model): "管理员": rec.admin_id.name if rec.admin_id else "", }) - return { - "rows": rows, - "summary": { - "序号": False, - "应收租期": False, - "承租方名称": "合计:", - "金额(含税)": round(total_amount, 2), - "税率": False, - "金额(不含税)": round(total_amount_nt, 2), - "物业名称": False, - "产权证": False, - "合同编号": False, - "管理员": False, - } - } + # 构造合计数据:将各分类合计作为独立的统计数据返回 + summary_stats = [] + # 按管理类型名称排序 + for type_name in sorted(type_totals.keys()): + totals = type_totals[type_name] + summary_stats.append({ + 'name': type_name, + 'amount': round(totals['amount'], 2), + 'amount_nt': round(totals['amount_nt'], 2) + }) + # 添加总合计卡片 + summary_stats.append({'name': '总合计', 'amount': round(total_amount, 2), 'amount_nt': round(total_amount_nt, 2)}) + + return {"rows": rows, "stats": summary_stats} diff --git a/yuthon_report/models/current_receivable_rent_report.py b/yuthon_report/models/current_receivable_rent_report.py index 5618afe7f..e33e1998c 100644 --- a/yuthon_report/models/current_receivable_rent_report.py +++ b/yuthon_report/models/current_receivable_rent_report.py @@ -105,25 +105,17 @@ class CurrentReceivableRentReport(models.Model): 'amount': round(totals['amount'], 2), 'amount_nt': round(totals['amount_nt'], 2) }) + + # 添加总合计卡片 + summary_stats.append({ + 'name': '总合计', + 'amount': round(total_all_amount, 2), + 'amount_nt': round(total_all_amount_nt, 2) + }) - # 返回字典格式,包含数据行、简化的表格合计行、独立的统计卡片数据 + # 返回字典格式,只包含数据行和统计卡片数据 return { "rows": rows, - "summary": { - "序号": "", - "应收开始": "", - "应收结束": "", - "承租方名称": "总合计:", - "原始金额": "", - "减免金额": "", - "实际金额(含税)": round(total_all_amount, 2), - "税率": "", - "实际金额(不含税)": round(total_all_amount_nt, 2), - "物业名称": "", - "产权证": "", - "合同编号": "", - "管理员": "", - }, - "stats": summary_stats # 新增统计卡片数据 + "stats": summary_stats } diff --git a/yuthon_report/models/deposit_total_report.py b/yuthon_report/models/deposit_total_report.py index dd16335bc..494d4c81e 100644 --- a/yuthon_report/models/deposit_total_report.py +++ b/yuthon_report/models/deposit_total_report.py @@ -45,16 +45,24 @@ class DepositTotalReport(models.Model): else: type_domain = domain[:] - receivables = self.search(type_domain, limit=3000, order="due_date asc") + receivables = self.search(type_domain, order="due_date asc") if not receivables and type_domain != domain: - receivables = self.search(domain, limit=3000, order="due_date asc") + receivables = self.search(domain, order="due_date asc") rows = [] + # 按管理类型分组统计 + type_totals = {} # {'管理类型名称': {'amount': 0}} total_amount = 0.0 for idx, rec in enumerate(receivables, start=1): amount = rec.original_amount or 0.0 total_amount += amount + + # 统计每个管理类型的合计 + type_name = rec.management_type_id.name if rec.management_type_id else "未分类" + if type_name not in type_totals: + type_totals[type_name] = {'amount': 0.0} + type_totals[type_name]['amount'] += amount rows.append({ "序号": idx, "合同编号": rec.contract_id.contract_code if rec.contract_id else "", @@ -65,16 +73,17 @@ class DepositTotalReport(models.Model): "管理员": rec.admin_id.name if rec.admin_id else "", }) - return { - "rows": rows, - "summary": { - "序号": False, - "合同编号": False, - "物业名称": False, - "承租方名称": "合计:", - "金额": round(total_amount, 2), - "所属公司": False, - "管理员": False, - } - } + # 构造合计数据:将各分类合计作为独立的统计数据返回 + summary_stats = [] + # 按管理类型名称排序 + for type_name in sorted(type_totals.keys()): + totals = type_totals[type_name] + summary_stats.append({ + 'name': type_name, + 'amount': round(totals['amount'], 2), + }) + # 添加总合计卡片 + summary_stats.append({'name': '总合计', 'amount': round(total_amount, 2)}) + + return {"rows": rows, "stats": summary_stats} diff --git a/yuthon_report/models/monthly_assessment_fee_report.py b/yuthon_report/models/monthly_assessment_fee_report.py index ec4214bf1..1319ff520 100644 --- a/yuthon_report/models/monthly_assessment_fee_report.py +++ b/yuthon_report/models/monthly_assessment_fee_report.py @@ -72,17 +72,22 @@ class MonthlyAssessmentFeeReport(models.Model): else: type_domain = base_domain[:] - receivables = self.search(type_domain + date_domain, limit=2000, order="payout_date asc, due_date asc") + receivables = self.search(type_domain + date_domain, order="payout_date asc, due_date asc") if not receivables and type_domain != base_domain: - receivables = self.search(base_domain + date_domain, limit=2000, order="payout_date asc, due_date asc") + receivables = self.search(base_domain + date_domain, order="payout_date asc, due_date asc") if not receivables and type_domain != base_domain: - receivables = self.search(base_domain, limit=2000, order="payout_date asc, due_date asc") + receivables = self.search(base_domain, order="payout_date asc, due_date asc") rows = [] + type_totals = {} total_amount = 0.0 for idx, rec in enumerate(receivables, start=1): amount = rec.original_amount or 0.0 total_amount += amount + type_name = rec.management_type_id.name if rec.management_type_id else "未分类" + if type_name not in type_totals: + type_totals[type_name] = {'amount': 0.0} + type_totals[type_name]['amount'] += amount pay_date = rec.payout_date or rec.due_date rows.append({ "序号": idx, @@ -95,17 +100,10 @@ class MonthlyAssessmentFeeReport(models.Model): "管理员": rec.admin_id.name if rec.admin_id else "", }) - return { - "rows": rows, - "summary": { - "序号": False, - "编码": False, - "日期": False, - "物业名称": False, - "金额": round(total_amount, 2), - "备注": False, - "所属公司": False, - "管理员": False, - } - } + summary_stats = [] + for type_name in sorted(type_totals.keys()): + summary_stats.append({'name': type_name, 'amount': round(type_totals[type_name]['amount'], 2)}) + summary_stats.append({'name': '总合计', 'amount': round(total_amount, 2)}) + + return {"rows": rows, "stats": summary_stats} diff --git a/yuthon_report/models/monthly_maintenance_fee_report.py b/yuthon_report/models/monthly_maintenance_fee_report.py index f31fdf03f..f09329be9 100644 --- a/yuthon_report/models/monthly_maintenance_fee_report.py +++ b/yuthon_report/models/monthly_maintenance_fee_report.py @@ -72,17 +72,22 @@ class MonthlyMaintenanceFeeReport(models.Model): else: type_domain = base_domain[:] - receivables = self.search(type_domain + date_domain, limit=3000, order="payout_date asc, due_date asc") + receivables = self.search(type_domain + date_domain, order="payout_date asc, due_date asc") if not receivables and type_domain != base_domain: - receivables = self.search(base_domain + date_domain, limit=3000, order="payout_date asc, due_date asc") + receivables = self.search(base_domain + date_domain, order="payout_date asc, due_date asc") if not receivables and type_domain != base_domain: - receivables = self.search(base_domain, limit=3000, order="payout_date asc, due_date asc") + receivables = self.search(base_domain, order="payout_date asc, due_date asc") rows = [] + type_totals = {} total_amount = 0.0 for idx, rec in enumerate(receivables, start=1): amount = rec.original_amount or 0.0 total_amount += amount + type_name = rec.management_type_id.name if rec.management_type_id else "未分类" + if type_name not in type_totals: + type_totals[type_name] = {'amount': 0.0} + type_totals[type_name]['amount'] += amount pay_date = rec.payout_date or rec.due_date rows.append({ "序号": idx, @@ -95,17 +100,10 @@ class MonthlyMaintenanceFeeReport(models.Model): "管理员": rec.admin_id.name if rec.admin_id else "", }) - return { - "rows": rows, - "summary": { - "序号": False, - "编码": False, - "日期": False, - "物业名称": False, - "金额": round(total_amount, 2), - "备注": False, - "所属公司": False, - "管理员": False, - } - } + summary_stats = [] + for type_name in sorted(type_totals.keys()): + summary_stats.append({'name': type_name, 'amount': round(type_totals[type_name]['amount'], 2)}) + summary_stats.append({'name': '总合计', 'amount': round(total_amount, 2)}) + + return {"rows": rows, "stats": summary_stats} diff --git a/yuthon_report/models/monthly_management_fee_report.py b/yuthon_report/models/monthly_management_fee_report.py index 5a1848d99..9bfc6d0e1 100644 --- a/yuthon_report/models/monthly_management_fee_report.py +++ b/yuthon_report/models/monthly_management_fee_report.py @@ -73,17 +73,25 @@ class MonthlyManagementFeeReport(models.Model): else: type_domain = base_domain[:] - receivables = self.search(type_domain + date_domain, limit=2000, order="payout_date asc, due_date asc") + receivables = self.search(type_domain + date_domain, order="payout_date asc, due_date asc") if not receivables and type_domain != base_domain: - receivables = self.search(base_domain + date_domain, limit=2000, order="payout_date asc, due_date asc") + receivables = self.search(base_domain + date_domain, order="payout_date asc, due_date asc") if not receivables and type_domain != base_domain: - receivables = self.search(base_domain, limit=2000, order="payout_date asc, due_date asc") + receivables = self.search(base_domain, order="payout_date asc, due_date asc") rows = [] + # 按管理类型分组统计 + type_totals = {} # {'管理类型名称': {'amount': 0}} total_amount = 0.0 for idx, rec in enumerate(receivables, start=1): amount = rec.original_amount or 0.0 total_amount += amount + + # 统计每个管理类型的合计 + type_name = rec.management_type_id.name if rec.management_type_id else "未分类" + if type_name not in type_totals: + type_totals[type_name] = {'amount': 0.0} + type_totals[type_name]['amount'] += amount pay_date = rec.payout_date or rec.due_date rows.append({ "序号": idx, @@ -96,17 +104,12 @@ class MonthlyManagementFeeReport(models.Model): "管理员": rec.admin_id.name if rec.admin_id else "", }) - return { - "rows": rows, - "summary": { - "序号": False, - "编码": False, - "日期": False, - "物业名称": False, - "金额": round(total_amount, 2), - "备注": False, - "所属公司": False, - "管理员": False, - } - } + # 构造合计数据 + summary_stats = [] + for type_name in sorted(type_totals.keys()): + totals = type_totals[type_name] + summary_stats.append({'name': type_name, 'amount': round(totals['amount'], 2)}) + summary_stats.append({'name': '总合计', 'amount': round(total_amount, 2)}) + + return {"rows": rows, "stats": summary_stats} diff --git a/yuthon_report/models/monthly_other_expense_report.py b/yuthon_report/models/monthly_other_expense_report.py index 8ff7cdd58..310350ff4 100644 --- a/yuthon_report/models/monthly_other_expense_report.py +++ b/yuthon_report/models/monthly_other_expense_report.py @@ -72,17 +72,22 @@ class MonthlyOtherExpenseReport(models.Model): else: type_domain = base_domain[:] - receivables = self.search(type_domain + date_domain, limit=3000, order="payout_date asc, due_date asc") + receivables = self.search(type_domain + date_domain, order="payout_date asc, due_date asc") if not receivables and type_domain != base_domain: - receivables = self.search(base_domain + date_domain, limit=3000, order="payout_date asc, due_date asc") + receivables = self.search(base_domain + date_domain, order="payout_date asc, due_date asc") if not receivables and type_domain != base_domain: - receivables = self.search(base_domain, limit=3000, order="payout_date asc, due_date asc") + receivables = self.search(base_domain, order="payout_date asc, due_date asc") rows = [] + type_totals = {} total_amount = 0.0 for idx, rec in enumerate(receivables, start=1): amount = rec.original_amount or 0.0 total_amount += amount + type_name = rec.management_type_id.name if rec.management_type_id else "未分类" + if type_name not in type_totals: + type_totals[type_name] = {'amount': 0.0} + type_totals[type_name]['amount'] += amount pay_date = rec.payout_date or rec.due_date rows.append({ "序号": idx, @@ -95,17 +100,10 @@ class MonthlyOtherExpenseReport(models.Model): "管理员": rec.admin_id.name if rec.admin_id else "", }) - return { - "rows": rows, - "summary": { - "序号": False, - "编码": False, - "日期": False, - "物业名称": False, - "金额": round(total_amount, 2), - "备注": False, - "所属公司": False, - "管理员": False, - } - } + summary_stats = [] + for type_name in sorted(type_totals.keys()): + summary_stats.append({'name': type_name, 'amount': round(type_totals[type_name]['amount'], 2)}) + summary_stats.append({'name': '总合计', 'amount': round(total_amount, 2)}) + + return {"rows": rows, "stats": summary_stats} diff --git a/yuthon_report/models/overdue_amount_report.py b/yuthon_report/models/overdue_amount_report.py index c1ffe7937..346a2bda0 100644 --- a/yuthon_report/models/overdue_amount_report.py +++ b/yuthon_report/models/overdue_amount_report.py @@ -32,8 +32,9 @@ class OverdueAmountReport(models.Model): if management_type_id: domain.append(('management_type_id', '=', management_type_id)) - receivables = self.search(domain, limit=1000) + receivables = self.search(domain) sum_dict = {} + type_totals = {} for rec in receivables: # 提取基础数据 @@ -44,9 +45,15 @@ class OverdueAmountReport(models.Model): rent = round(rec.contract_id.rent, 2) if (rec.contract_id and rec.contract_id.rent) else 0.0 submitted = round(rec.submitted, 2) if rec.submitted else 0.0 due_month = rec.due_date.strftime("%Y-%m-%d") if rec.due_date else "" - management_type_id = rec.management_type_id.name - lessor_referred = rec.contract_id.lessor_id.referred - admin_name = rec.contract_id.admin_id.name + management_type_id = rec.management_type_id.name if rec.management_type_id else "未分类" + lessor_referred = rec.contract_id.lessor_id.referred if rec.contract_id and rec.contract_id.lessor_id and hasattr(rec.contract_id.lessor_id, 'referred') else rec.contract_id.lessor_id.name if rec.contract_id and rec.contract_id.lessor_id else "" + admin_name = rec.contract_id.admin_id.name if rec.contract_id and rec.contract_id.admin_id else "" + + # 统计每个管理类型的合计 + if management_type_id not in type_totals: + type_totals[management_type_id] = {'amount': 0.0, 'arrears': 0.0} + type_totals[management_type_id]['amount'] += rent + type_totals[management_type_id]['arrears'] += submitted # 构建唯一标识key(合同编号 + 欠费日期) key = (contract_code, due_month) @@ -67,7 +74,23 @@ class OverdueAmountReport(models.Model): "管理员": admin_name } - return list(sum_dict.values()) + rows = [] + total_amount = 0.0 + total_arrears = 0.0 + for idx, item in enumerate(sum_dict.values(), start=1): + rows.append({ + "序号": idx, + **item + }) + total_amount += item["金额"] + total_arrears += item["欠费金额"] + + summary_stats = [] + for type_name in sorted(type_totals.keys()): + summary_stats.append({'name': type_name, 'amount': round(type_totals[type_name]['arrears'], 2)}) + summary_stats.append({'name': '总合计', 'amount': round(total_arrears, 2)}) + + return {"rows": rows, "stats": summary_stats} @api.model def get_domain(self, start_date=False, end_date=False, company_id=False): diff --git a/yuthon_report/models/penalty_detail_report.py b/yuthon_report/models/penalty_detail_report.py index 442228643..d5b461748 100644 --- a/yuthon_report/models/penalty_detail_report.py +++ b/yuthon_report/models/penalty_detail_report.py @@ -60,11 +60,13 @@ class PenaltyDetailReport(models.Model): date_domain = ['|', ('due_date', '<=', end_dt), ('payout_date', '<=', end_dt)] # 先按日期过滤查,如果无结果且传了日期,则去掉日期过滤再查一遍,避免漏数据 - receivables = self.search(domain + date_domain, limit=2000, order="due_date asc") + receivables = self.search(domain + date_domain, order="due_date asc") if not receivables and date_domain: - receivables = self.search(domain, limit=2000, order="due_date asc") + receivables = self.search(domain, order="due_date asc") rows = [] + # 按管理类型分组统计 + type_totals = {} # {'管理类型名称': {...}} total_amount = total_penalty = total_owed = total_collected = total_uncollected = 0.0 total_per_day = 0.0 @@ -85,6 +87,20 @@ class PenaltyDetailReport(models.Model): total_collected += collected total_uncollected += uncollected total_per_day += per_day + + # 统计每个管理类型的合计 + type_name = rec.management_type_id.name if rec.management_type_id else "未分类" + if type_name not in type_totals: + type_totals[type_name] = { + 'amount': 0.0, 'penalty': 0.0, 'owed': 0.0, + 'collected': 0.0, 'uncollected': 0.0, 'per_day': 0.0 + } + type_totals[type_name]['amount'] += amount + type_totals[type_name]['penalty'] += penalty_amount + type_totals[type_name]['owed'] += rent_owed + type_totals[type_name]['collected'] += collected + type_totals[type_name]['uncollected'] += uncollected + type_totals[type_name]['per_day'] += per_day rows.append({ "序号": idx, @@ -105,25 +121,22 @@ class PenaltyDetailReport(models.Model): "管理员": rec.admin_id.name if rec.admin_id else "", }) - return { - "rows": rows, - "summary": { - "序号": False, - "合同编号": "合计", - "物业名称": False, - "承租方名称": False, - "金额": round(total_amount, 2), - "违约金计费时间(天)": False, - "欠租金额": round(total_owed, 2), - "每天违约金(元/天)": round(total_per_day, 2), - "违约金金额": round(total_penalty, 2), - "总已收金额": round(total_collected, 2), - "总未收金额": round(total_uncollected, 2), - "当月收款时间": False, - "当月收款金额": round(total_collected, 2), - "所属公司": False, - "产权证号": False, - "管理员": False, - } - } + # 构造合计数据:将各分类合计作为独立的统计数据返回 + summary_stats = [] + # 按管理类型名称排序 + for type_name in sorted(type_totals.keys()): + totals = type_totals[type_name] + summary_stats.append({ + 'name': type_name, + 'amount': round(totals['amount'], 2), + 'penalty': round(totals['penalty'], 2), + }) + # 添加总合计卡片 + summary_stats.append({ + 'name': '总合计', + 'amount': round(total_owed, 2), + 'penalty': round(total_penalty, 2) + }) + + return {"rows": rows, "stats": summary_stats} diff --git a/yuthon_report/models/receivable_report.py b/yuthon_report/models/receivable_report.py index ff047f8de..38e7d3448 100644 --- a/yuthon_report/models/receivable_report.py +++ b/yuthon_report/models/receivable_report.py @@ -38,28 +38,46 @@ class YuthonProperty(models.Model): if company_id: domain.append(('company_id', '=', company_id)) - print(domain) - receivables = self.search(domain, limit=1000) + receivables = self.search(domain) management_type_map = dict( self.env['yuthon.management.type'].search([]).mapped(lambda r: (r.id, r.name)) ) invoice_type_map = dict( self._fields['invoice_type']._description_selection(self.env) ) - return [ - { - "应收类型": management_type_map.get(rec.management_type_id.id, "") if rec.management_type_id else "", + + rows = [] + type_totals = {} + total_amount = 0.0 + + for idx, rec in enumerate(receivables, start=1): + mgmt_type = management_type_map.get(rec.management_type_id.id, "") if rec.management_type_id else "未分类" + amount = rec.original_amount or 0.0 + total_amount += amount + + if mgmt_type not in type_totals: + type_totals[mgmt_type] = {'amount': 0.0} + type_totals[mgmt_type]['amount'] += amount + + rows.append({ + "序号": idx, + "应收类型": mgmt_type, "应收开始": rec.due_date.strftime("%Y-%m-%d") if rec.due_date else "", "应收结束": rec.due_date_end.strftime("%Y-%m-%d") if rec.due_date_end else "", "承租方": rec.lessee_id.name or "" if rec.lessee_id else "", - "当期金额": rec.original_amount or 0.0, + "当期金额": round(amount, 2), "物业名称": rec.property_name or "", "产权证号": rec.title_deed or "", "发票类型": invoice_type_map.get(rec.invoice_type, ""), "税率": f"{rec.tax}%" if rec.tax else "0%" - } - for rec in receivables - ] + }) + + summary_stats = [] + for type_name in sorted(type_totals.keys()): + summary_stats.append({'name': type_name, 'amount': round(type_totals[type_name]['amount'], 2)}) + summary_stats.append({'name': '总合计', 'amount': round(total_amount, 2)}) + + return {"rows": rows, "stats": summary_stats} @api.model def get_domain(self, start_date=False, end_date=False, company_id=False): diff --git a/yuthon_report/models/yearly_assessment_fee_report.py b/yuthon_report/models/yearly_assessment_fee_report.py index 24864f09f..d0d114481 100644 --- a/yuthon_report/models/yearly_assessment_fee_report.py +++ b/yuthon_report/models/yearly_assessment_fee_report.py @@ -68,17 +68,22 @@ class YearlyAssessmentFeeReport(models.Model): else: type_domain = base_domain[:] - receivables = self.search(type_domain + date_domain, limit=3000, order="payout_date asc, due_date asc") + receivables = self.search(type_domain + date_domain, order="payout_date asc, due_date asc") if not receivables and type_domain != base_domain: - receivables = self.search(base_domain + date_domain, limit=3000, order="payout_date asc, due_date asc") + receivables = self.search(base_domain + date_domain, order="payout_date asc, due_date asc") if not receivables and type_domain != base_domain: - receivables = self.search(base_domain, limit=3000, order="payout_date asc, due_date asc") + receivables = self.search(base_domain, order="payout_date asc, due_date asc") rows = [] + type_totals = {} total_amount = 0.0 for idx, rec in enumerate(receivables, start=1): amount = rec.original_amount or 0.0 total_amount += amount + type_name = rec.management_type_id.name if rec.management_type_id else "未分类" + if type_name not in type_totals: + type_totals[type_name] = {'amount': 0.0} + type_totals[type_name]['amount'] += amount pay_date = rec.payout_date or rec.due_date rows.append({ "序号": idx, @@ -91,17 +96,10 @@ class YearlyAssessmentFeeReport(models.Model): "备注": rec.remark or "", }) - return { - "rows": rows, - "summary": { - "序号": False, - "编码": False, - "日期": False, - "物业名称": False, - "管理员": "合计:", - "金额": round(total_amount, 2), - "所属公司": False, - "备注": False, - } - } + summary_stats = [] + for type_name in sorted(type_totals.keys()): + summary_stats.append({'name': type_name, 'amount': round(type_totals[type_name]['amount'], 2)}) + summary_stats.append({'name': '总合计', 'amount': round(total_amount, 2)}) + + return {"rows": rows, "stats": summary_stats} diff --git a/yuthon_report/models/yearly_maintenance_fee_report.py b/yuthon_report/models/yearly_maintenance_fee_report.py index 55d89adeb..69f5a20d0 100644 --- a/yuthon_report/models/yearly_maintenance_fee_report.py +++ b/yuthon_report/models/yearly_maintenance_fee_report.py @@ -68,17 +68,22 @@ class YearlyMaintenanceFeeReport(models.Model): else: type_domain = base_domain[:] - receivables = self.search(type_domain + date_domain, limit=3000, order="payout_date asc, due_date asc") + receivables = self.search(type_domain + date_domain, order="payout_date asc, due_date asc") if not receivables and type_domain != base_domain: - receivables = self.search(base_domain + date_domain, limit=3000, order="payout_date asc, due_date asc") + receivables = self.search(base_domain + date_domain, order="payout_date asc, due_date asc") if not receivables and type_domain != base_domain: - receivables = self.search(base_domain, limit=3000, order="payout_date asc, due_date asc") + receivables = self.search(base_domain, order="payout_date asc, due_date asc") rows = [] + type_totals = {} total_amount = 0.0 for idx, rec in enumerate(receivables, start=1): amount = rec.original_amount or 0.0 total_amount += amount + type_name = rec.management_type_id.name if rec.management_type_id else "未分类" + if type_name not in type_totals: + type_totals[type_name] = {'amount': 0.0} + type_totals[type_name]['amount'] += amount pay_date = rec.payout_date or rec.due_date rows.append({ "序号": idx, @@ -91,17 +96,10 @@ class YearlyMaintenanceFeeReport(models.Model): "备注": rec.remark or "", }) - return { - "rows": rows, - "summary": { - "序号": False, - "编码": False, - "支出日期": False, - "物业名称": False, - "管理员": "合计:", - "金额": round(total_amount, 2), - "所属公司": False, - "备注": False, - } - } + summary_stats = [] + for type_name in sorted(type_totals.keys()): + summary_stats.append({'name': type_name, 'amount': round(type_totals[type_name]['amount'], 2)}) + summary_stats.append({'name': '总合计', 'amount': round(total_amount, 2)}) + + return {"rows": rows, "stats": summary_stats} diff --git a/yuthon_report/models/yearly_management_fee_report.py b/yuthon_report/models/yearly_management_fee_report.py index 26a4ce2e9..d18ef474d 100644 --- a/yuthon_report/models/yearly_management_fee_report.py +++ b/yuthon_report/models/yearly_management_fee_report.py @@ -70,17 +70,25 @@ class YearlyManagementFeeReport(models.Model): else: type_domain = base_domain[:] - receivables = self.search(type_domain + date_domain, limit=2000, order="payout_date asc, due_date asc") + receivables = self.search(type_domain + date_domain, order="payout_date asc, due_date asc") if not receivables and type_domain != base_domain: - receivables = self.search(base_domain + date_domain, limit=2000, order="payout_date asc, due_date asc") + receivables = self.search(base_domain + date_domain, order="payout_date asc, due_date asc") if not receivables and type_domain != base_domain: - receivables = self.search(base_domain, limit=2000, order="payout_date asc, due_date asc") + receivables = self.search(base_domain, order="payout_date asc, due_date asc") rows = [] + # 按管理类型分组统计 + type_totals = {} # {'管理类型名称': {'amount': 0}} total_amount = 0.0 for idx, rec in enumerate(receivables, start=1): amount = rec.original_amount or 0.0 total_amount += amount + + # 统计每个管理类型的合计 + type_name = rec.management_type_id.name if rec.management_type_id else "未分类" + if type_name not in type_totals: + type_totals[type_name] = {'amount': 0.0} + type_totals[type_name]['amount'] += amount pay_date = rec.payout_date or rec.due_date rows.append({ "序号": idx, @@ -93,17 +101,12 @@ class YearlyManagementFeeReport(models.Model): "备注": rec.remark or "", }) - return { - "rows": rows, - "summary": { - "序号": False, - "编码": False, - "支出日期": False, - "物业名称": False, - "管理员": "合计:", - "金额": round(total_amount, 2), - "所属公司": False, - "备注": False, - } - } + # 构造合计数据 + summary_stats = [] + for type_name in sorted(type_totals.keys()): + totals = type_totals[type_name] + summary_stats.append({'name': type_name, 'amount': round(totals['amount'], 2)}) + summary_stats.append({'name': '总合计', 'amount': round(total_amount, 2)}) + + return {"rows": rows, "stats": summary_stats} diff --git a/yuthon_report/models/yearly_other_fee_report.py b/yuthon_report/models/yearly_other_fee_report.py index 33fa1b490..c57f3ff08 100644 --- a/yuthon_report/models/yearly_other_fee_report.py +++ b/yuthon_report/models/yearly_other_fee_report.py @@ -68,17 +68,22 @@ class YearlyOtherFeeReport(models.Model): else: type_domain = base_domain[:] - receivables = self.search(type_domain + date_domain, limit=3000, order="payout_date asc, due_date asc") + receivables = self.search(type_domain + date_domain, order="payout_date asc, due_date asc") if not receivables and type_domain != base_domain: - receivables = self.search(base_domain + date_domain, limit=3000, order="payout_date asc, due_date asc") + receivables = self.search(base_domain + date_domain, order="payout_date asc, due_date asc") if not receivables and type_domain != base_domain: - receivables = self.search(base_domain, limit=3000, order="payout_date asc, due_date asc") + receivables = self.search(base_domain, order="payout_date asc, due_date asc") rows = [] + type_totals = {} total_amount = 0.0 for idx, rec in enumerate(receivables, start=1): amount = rec.original_amount or 0.0 total_amount += amount + type_name = rec.management_type_id.name if rec.management_type_id else "未分类" + if type_name not in type_totals: + type_totals[type_name] = {'amount': 0.0} + type_totals[type_name]['amount'] += amount pay_date = rec.payout_date or rec.due_date rows.append({ "序号": idx, @@ -91,17 +96,10 @@ class YearlyOtherFeeReport(models.Model): "备注": rec.remark or "", }) - return { - "rows": rows, - "summary": { - "序号": False, - "编码": False, - "支出日期": False, - "物业名称": False, - "管理员": "合计:", - "金额": round(total_amount, 2), - "所属公司": False, - "备注": False, - } - } + summary_stats = [] + for type_name in sorted(type_totals.keys()): + summary_stats.append({'name': type_name, 'amount': round(type_totals[type_name]['amount'], 2)}) + summary_stats.append({'name': '总合计', 'amount': round(total_amount, 2)}) + + return {"rows": rows, "stats": summary_stats} diff --git a/yuthon_report/models/yearly_penalty_report.py b/yuthon_report/models/yearly_penalty_report.py index 2de089c83..0d95cc0b7 100644 --- a/yuthon_report/models/yearly_penalty_report.py +++ b/yuthon_report/models/yearly_penalty_report.py @@ -62,10 +62,18 @@ class YearlyPenaltyReport(models.Model): receivables = self.search(domain, order="due_date asc") rows = [] + # 按管理类型分组统计 + type_totals = {} # {'管理类型名称': {'amount': 0}} total_amount = 0.0 for idx, rec in enumerate(receivables, start=1): amount = rec.penalty or rec.submitted or rec.original_amount or 0.0 total_amount += amount + + # 统计每个管理类型的合计 + type_name = rec.management_type_id.name if rec.management_type_id else "未分类" + if type_name not in type_totals: + type_totals[type_name] = {'amount': 0.0} + type_totals[type_name]['amount'] += amount rows.append({ "序号": idx, "合同编号": rec.contract_id.contract_code if rec.contract_id else "", @@ -76,16 +84,12 @@ class YearlyPenaltyReport(models.Model): "管理员": rec.admin_id.name if rec.admin_id else "", }) - return { - "rows": rows, - "summary": { - "序号": False, - "合同编号": False, - "物业名称": False, - "承租方名称": "合计:", - "金额": round(total_amount, 2), - "所属公司": False, - "管理员": False, - } - } + # 构造合计数据 + summary_stats = [] + for type_name in sorted(type_totals.keys()): + totals = type_totals[type_name] + summary_stats.append({'name': type_name, 'amount': round(totals['amount'], 2)}) + summary_stats.append({'name': '总合计', 'amount': round(total_amount, 2)}) + + return {"rows": rows, "stats": summary_stats} 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 f0be211cf..ac112b13a 100644 --- a/yuthon_report/static/src/components/yuthon_report/receivable_report.js +++ b/yuthon_report/static/src/components/yuthon_report/receivable_report.js @@ -226,19 +226,21 @@ class ReceivableReport extends Component { ); console.log(result) - // 处理返回的数据结构,支持固定底部合计行 - if (result && typeof result === 'object' && result.rows && result.summary) { - // 新格式:分离数据行和合计行 - this.grid.setGridOption('rowData', result.rows); - // summary可能是单个对象或数组 - const summaryData = Array.isArray(result.summary) ? result.summary : [result.summary]; - this.grid.setGridOption('pinnedBottomRowData', summaryData); - // 更新统计卡片数据 - this.state.stats = result.stats || []; - } else { - // 兼容旧格式:数组 - this.grid.setGridOption('rowData', result); - this.state.stats = []; + // 处理返回的数据结构 + if (result && typeof result === 'object') { + if (result.rows) { + // 新格式:分离数据行和统计数据 + this.grid.setGridOption('rowData', result.rows); + // 取消固定底部合计行 + this.grid.setGridOption('pinnedBottomRowData', []); + // 更新统计卡片数据 + this.state.stats = result.stats || []; + } else { + // 兼容旧格式:直接是数组 + this.grid.setGridOption('rowData', result); + this.grid.setGridOption('pinnedBottomRowData', []); + this.state.stats = []; + } } }