diff --git a/yuthon_report/models/cum_arrears_report.py b/yuthon_report/models/cum_arrears_report.py index 191de0764..0360f83b8 100644 --- a/yuthon_report/models/cum_arrears_report.py +++ b/yuthon_report/models/cum_arrears_report.py @@ -25,6 +25,7 @@ class CumArrearsReport(models.Model): date_start = params.get("date_start") date_end = params.get("date_end") + summary_only = params.get("summary_only", False) # 查询月范围:query_month_start = 查询日所在月的月初,query_month_end = 查询日所在月的月末 # 注意:date_end 传的是用户选的某一天(如月初1号),不是月末 # 所以 query_month_end 要用该月的最后一天,否则同月收款会被误判为欠收 @@ -101,6 +102,13 @@ class CumArrearsReport(models.Model): rows = [] total_amount = 0.0 + if summary_only: + total_amount = sum(round(data['amount'], 2) for data in rec_map.values()) + stats = [ + {'name': '记录总数', 'amount': len(rec_map)}, + {'name': '累计欠收', 'amount': round(total_amount, 2)}, + ] + return {"rows": [], "stats": stats} for idx, (rid, data) in enumerate(rec_map.items(), start=1): rec = data['rec'] contract = rec.contract_id diff --git a/yuthon_report/models/cum_income_report.py b/yuthon_report/models/cum_income_report.py index 6bb5c6a4b..2c46d64fb 100644 --- a/yuthon_report/models/cum_income_report.py +++ b/yuthon_report/models/cum_income_report.py @@ -20,6 +20,7 @@ class CumIncomeReport(models.Model): today = datetime.date.today() date_start = params.get("date_start") + summary_only = params.get("summary_only", False) # 取查询日所在年份和月份 if date_start: query_date = datetime.datetime.fromisoformat(date_start).date() @@ -70,6 +71,13 @@ class CumIncomeReport(models.Model): rows = [] total_amount = 0.0 + if summary_only: + total_amount = sum(round(line.amount or 0.0, 2) for line in lines) + stats = [ + {'name': '记录总数', 'amount': len(lines)}, + {'name': '累计收入', 'amount': round(total_amount, 2)}, + ] + return {"rows": [], "stats": stats} for idx, line in enumerate(lines, start=1): rec = line.receivable_id contract = rec.contract_id if rec else None diff --git a/yuthon_report/models/current_month_received_report.py b/yuthon_report/models/current_month_received_report.py index f5402c1ce..ae5246af2 100644 --- a/yuthon_report/models/current_month_received_report.py +++ b/yuthon_report/models/current_month_received_report.py @@ -9,6 +9,7 @@ class CurrentMonthReceivedReport(models.Model): @api.model def get_current_month_received_report(self, params): """归属本月实收报表:按收款行自身的 reconciliation_date(对账日期)筛选已收金额。""" + summary_only = params.get("summary_only", False) domain = [] keyword = params.get("keyword") @@ -89,6 +90,11 @@ class CurrentMonthReceivedReport(models.Model): continue # 这笔归到本月预收,不计入归属本月 filtered_lines.append(line) + if summary_only: + total_amount = sum(line.amount or 0.0 for line in filtered_lines) + stats = [{"name": "实收总额", "amount": round(total_amount, 2)}] + return {"rows": [], "stats": stats} + rows = [] total_amount = 0.0 diff --git a/yuthon_report/models/deposit_cum_arrears_report.py b/yuthon_report/models/deposit_cum_arrears_report.py index 516372a64..1f54dbcac 100644 --- a/yuthon_report/models/deposit_cum_arrears_report.py +++ b/yuthon_report/models/deposit_cum_arrears_report.py @@ -21,16 +21,23 @@ class DepositCumArrearsReport(models.Model): payout_date <= 查询月末 → 查询时点时已收到,不计入欠收 """ date_start = params.get("date_start") + summary_only = params.get("summary_only", False) query_date = datetime.datetime.fromisoformat(date_start).date() if date_start else datetime.date.today() month_first = query_date.replace(day=1) last_day = calendar.monthrange(query_date.year, query_date.month)[1] month_last = query_date.replace(day=last_day) - # 放宽 domain,只做基础筛选 + # 优化:将内存过滤条件尽量推到 domain 层,减少加载记录数 + # - unpaid/no_paid:due_date <= 查询月末(到期未收) + # - paid:payout_date > 查询月末(查询时点尚未收款)—— 把这个条件推到 domain domain = [ ('management_type_id.name', '=', '押金'), ('is_invalid', '!=', True), - ('deposit_state', 'in', ['unpaid', 'no_paid', 'paid']), + '|', + ('deposit_state', 'in', ['unpaid', 'no_paid']), + '&', + ('deposit_state', '=', 'paid'), + ('payout_date', '>', month_last), ] keyword = params.get("keyword") @@ -62,19 +69,25 @@ class DepositCumArrearsReport(models.Model): lines = self.sudo().search(domain, order="contract_id asc") - # 内存过滤:按状态分别处理 - # - 未收(unpaid)/收部分(no_paid):due_date 存在且 <= 查询月末(到期未收) - # - 已收(paid):payout_date > 查询月末(查询时点上尚未收款) + # 内存过滤:unpaid/no_paid 需要额外判断 due_date <= 查询月末 + # paid 的 payout_date > month_last 已在 domain 中处理 lines = lines.filtered( lambda l: ( l.deposit_state in ('unpaid', 'no_paid') and l.due_date and l.due_date <= month_last ) or ( - l.deposit_state == 'paid' and l.payout_date and l.payout_date > month_last + l.deposit_state == 'paid' ) ) rows = [] total_amount = 0.0 + if summary_only: + total_amount = sum(round(rec.submitted or 0.0, 2) for rec in lines) + stats = [ + {'name': '记录总数', 'amount': len(lines)}, + {'name': '合计欠收', 'amount': round(total_amount, 2)}, + ] + return {"rows": [], "stats": stats} for idx, rec in enumerate(lines, start=1): amount = round(rec.submitted or 0.0, 2) total_amount += amount diff --git a/yuthon_report/models/deposit_month_receivable_report.py b/yuthon_report/models/deposit_month_receivable_report.py index 861e32be9..d703226cd 100644 --- a/yuthon_report/models/deposit_month_receivable_report.py +++ b/yuthon_report/models/deposit_month_receivable_report.py @@ -17,6 +17,7 @@ class DepositMonthReceivableReport(models.Model): - 非作废记录 """ date_start = params.get("date_start") + summary_only = params.get("summary_only", False) query_date = datetime.datetime.fromisoformat(date_start).date() if date_start else datetime.date.today() month_first = query_date.replace(day=1) last_day = calendar.monthrange(query_date.year, query_date.month)[1] @@ -49,6 +50,14 @@ class DepositMonthReceivableReport(models.Model): lines = self.sudo().search(domain, order="contract_id asc") + if summary_only: + total_amount = sum(round(rec.original_amount or 0.0, 2) for rec in lines) + stats = [ + {'name': '记录总数', 'amount': len(lines)}, + {'name': '合计金额', 'amount': round(total_amount, 2)}, + ] + return {"rows": [], "stats": stats} + rows = [] total_amount = 0.0 for idx, rec in enumerate(lines, start=1): diff --git a/yuthon_report/models/deposit_month_received_report.py b/yuthon_report/models/deposit_month_received_report.py index 53dfb3f82..41c5993db 100644 --- a/yuthon_report/models/deposit_month_received_report.py +++ b/yuthon_report/models/deposit_month_received_report.py @@ -22,6 +22,7 @@ class DepositMonthReceivedReport(models.Model): 本月实收取 payout_date 在查询月内,避免同一记录出现在两个报表 """ date_start = params.get("date_start") + summary_only = params.get("summary_only", False) query_date = datetime.datetime.fromisoformat(date_start).date() if date_start else datetime.date.today() month_first = query_date.replace(day=1) last_day = calendar.monthrange(query_date.year, query_date.month)[1] @@ -75,6 +76,14 @@ class DepositMonthReceivedReport(models.Model): ) ) + if summary_only: + total_amount = sum(round((rec.original_amount or 0.0) - (rec.discount_amount or 0.0), 2) for rec in lines) + stats = [ + {'name': '记录总数', 'amount': len(lines)}, + {'name': '合计金额', 'amount': round(total_amount, 2)}, + ] + return {"rows": [], "stats": stats} + rows = [] total_amount = 0.0 for idx, rec in enumerate(lines, start=1): diff --git a/yuthon_report/models/deposit_month_refund_report.py b/yuthon_report/models/deposit_month_refund_report.py index 9586338e9..2fcd339c2 100644 --- a/yuthon_report/models/deposit_month_refund_report.py +++ b/yuthon_report/models/deposit_month_refund_report.py @@ -19,6 +19,7 @@ class DepositMonthRefundReport(models.Model): - 金额:original_amount - discount_amount """ date_start = params.get("date_start") + summary_only = params.get("summary_only", False) query_date = datetime.datetime.fromisoformat(date_start).date() if date_start else datetime.date.today() month_first = query_date.replace(day=1) last_day = calendar.monthrange(query_date.year, query_date.month)[1] @@ -64,6 +65,14 @@ class DepositMonthRefundReport(models.Model): lines = self.sudo().search(domain, order="contract_id asc") + if summary_only: + total_amount = sum(round((rec.original_amount or 0.0) - (rec.discount_amount or 0.0), 2) for rec in lines) + stats = [ + {'name': '记录总数', 'amount': len(lines)}, + {'name': '合计退款', 'amount': round(total_amount, 2)}, + ] + return {"rows": [], "stats": stats} + rows = [] total_amount = 0.0 for idx, rec in enumerate(lines, start=1): diff --git a/yuthon_report/models/deposit_pre_received_report.py b/yuthon_report/models/deposit_pre_received_report.py index 48e72839f..97c6373ea 100644 --- a/yuthon_report/models/deposit_pre_received_report.py +++ b/yuthon_report/models/deposit_pre_received_report.py @@ -22,6 +22,7 @@ class DepositPreReceivedReport(models.Model): (已退状态不受 payout_date 约束) """ date_start = params.get("date_start") + summary_only = params.get("summary_only", False) query_date = datetime.datetime.fromisoformat(date_start).date() if date_start else datetime.date.today() month_first = query_date.replace(day=1) _, last_day = calendar.monthrange(query_date.year, query_date.month) @@ -78,6 +79,14 @@ class DepositPreReceivedReport(models.Model): ) ) + if summary_only: + total_amount = sum(round((rec.original_amount or 0.0) - (rec.discount_amount or 0.0), 2) for rec in lines) + stats = [ + {'name': '记录总数', 'amount': len(lines)}, + {'name': '合计金额', 'amount': round(total_amount, 2)}, + ] + return {"rows": [], "stats": stats} + rows = [] total_amount = 0.0 for idx, rec in enumerate(lines, start=1): diff --git a/yuthon_report/models/deposit_total_report.py b/yuthon_report/models/deposit_total_report.py index 574e2cefc..71180e047 100644 --- a/yuthon_report/models/deposit_total_report.py +++ b/yuthon_report/models/deposit_total_report.py @@ -23,6 +23,7 @@ class DepositTotalReport(models.Model): (已退状态不受 payout_date 约束) """ date_start = params.get("date_start") + summary_only = params.get("summary_only", False) query_date = datetime.datetime.fromisoformat(date_start).date() if date_start else datetime.date.today() month_first = query_date.replace(day=1) last_day = calendar.monthrange(query_date.year, query_date.month)[1] @@ -100,6 +101,14 @@ class DepositTotalReport(models.Model): ) ) + if summary_only: + total_amount = sum(round((rec.original_amount or 0.0) - (rec.discount_amount or 0.0), 2) for rec in lines) + stats = [ + {'name': '记录总数', 'amount': len(lines)}, + {'name': '合计已收', 'amount': round(total_amount, 2)}, + ] + return {"rows": [], "stats": stats} + rows = [] total_amount = 0.0 for idx, rec in enumerate(lines, start=1): diff --git a/yuthon_report/models/leased_area_report.py b/yuthon_report/models/leased_area_report.py index 2159a66ab..62eab111a 100644 --- a/yuthon_report/models/leased_area_report.py +++ b/yuthon_report/models/leased_area_report.py @@ -47,6 +47,7 @@ class LeasedAreaReport(models.Model): property_id = params.get('property_id') lessee_id = params.get('lessee_id') contract_id = params.get('contract_id') + summary_only = params.get("summary_only", False) # ========== 二、查询日期解析 ========== query_date = None @@ -244,6 +245,7 @@ class LeasedAreaReport(models.Model): # ========== 八、第二轮遍历:构建报表行 + 计算合计 ========== rows = [] + row_count = 0 total_property_area = 0.0 # 物业已租面积合计 total_contract_area = 0.0 # 合同已租面积合计 counted_pids = set() # 已参与合计计算的物业id @@ -351,30 +353,32 @@ class LeasedAreaReport(models.Model): else: actual_end_str = "" - rows.append({ - "序号": idx, - "物业id": display_property_ids, - "物业名称": property_name_value, - "合同编号": { - 'model': 'property.lease.contract', - 'id': contract_record_id, - 'display_name': contract_code, - } if contract_record_id else contract_code or "", - "承租方": { - 'model': 'property.tenant.information', - 'id': lessee_record_id, - 'display_name': lessee_name, - } if lessee_record_id else lessee_name or "", - "实际开始时间": actual_start_str, - "实际结束日期": actual_end_str, - "物业已租面积": round(property_area, 4), - "合同已租面积": round(contract_area, 4), - "片区管理员": admin_display, - }) + if not summary_only: + rows.append({ + "序号": idx, + "物业id": display_property_ids, + "物业名称": property_name_value, + "合同编号": { + 'model': 'property.lease.contract', + 'id': contract_record_id, + 'display_name': contract_code, + } if contract_record_id else contract_code or "", + "承租方": { + 'model': 'property.tenant.information', + 'id': lessee_record_id, + 'display_name': lessee_name, + } if lessee_record_id else lessee_name or "", + "实际开始时间": actual_start_str, + "实际结束日期": actual_end_str, + "物业已租面积": round(property_area, 4), + "合同已租面积": round(contract_area, 4), + "片区管理员": admin_display, + }) + row_count += 1 # ========== 九、构建统计汇总数据 ========== stats = [ - {'name': '记录总数', 'amount': len(rows)}, + {'name': '记录总数', 'amount': row_count}, {'name': '物业已租面积合计', 'amount': round(total_property_area, 4), 'display_amount': f"{round(total_property_area, 4):.4f}"}, {'name': '合同已租面积合计', 'amount': round(total_contract_area, 4), 'display_amount': f"{round(total_contract_area, 4):.4f}"}, ] diff --git a/yuthon_report/models/management_area_report.py b/yuthon_report/models/management_area_report.py index 4fa51b899..c1b53645d 100644 --- a/yuthon_report/models/management_area_report.py +++ b/yuthon_report/models/management_area_report.py @@ -39,6 +39,7 @@ class ManagementAreaReport(models.Model): property_id = params.get('property_id') lessee_id = params.get('lessee_id') contract_id = params.get('contract_id') + summary_only = params.get("summary_only", False) # ========== 二、查询日期解析 ========== query_date = None @@ -126,6 +127,7 @@ class ManagementAreaReport(models.Model): # ========== 六、构建报表行 ========== rows = [] + row_count = 0 total_management_area = 0.0 seen_property_ids = set() # 已计入合计的物业id集合 @@ -219,33 +221,35 @@ class ManagementAreaReport(models.Model): seen_property_ids.add(property_rec.id) # --- 6.6 构建行数据(物业名称、合同编号、承租方支持穿透) --- - rows.append({ - "序号": idx, - "物业id": property_rec.id, - "物业名称": { - 'model': 'yuthon.property', - 'id': property_rec.id, - 'display_name': property_rec.name or "", - } if property_rec.name else "", - "合同编号": { - 'model': 'property.lease.contract', - 'id': contract_record_id, - 'display_name': contract_code, - } if contract_record_id and "、" not in contract_code else contract_code or "", - "承租方": { - 'model': 'property.tenant.information', - 'id': lessee_record_id, - 'display_name': lessee_name, - } if lessee_record_id and "、" not in lessee_name else lessee_name or "", - "产权证号": title_certificate, - "产权地址": property_address, - "管理面积": round(management_area, 4), - "片区管理员": admin_display, - }) + if not summary_only: + rows.append({ + "序号": idx, + "物业id": property_rec.id, + "物业名称": { + 'model': 'yuthon.property', + 'id': property_rec.id, + 'display_name': property_rec.name or "", + } if property_rec.name else "", + "合同编号": { + 'model': 'property.lease.contract', + 'id': contract_record_id, + 'display_name': contract_code, + } if contract_record_id and "、" not in contract_code else contract_code or "", + "承租方": { + 'model': 'property.tenant.information', + 'id': lessee_record_id, + 'display_name': lessee_name, + } if lessee_record_id and "、" not in lessee_name else lessee_name or "", + "产权证号": title_certificate, + "产权地址": property_address, + "管理面积": round(management_area, 4), + "片区管理员": admin_display, + }) + row_count += 1 # ========== 七、构建统计汇总数据 ========== stats = [ - {'name': '记录总数', 'amount': len(rows)}, + {'name': '记录总数', 'amount': row_count}, {'name': '管理面积合计', 'amount': round(total_management_area, 4), 'display_amount': f"{round(total_management_area, 4):.4f}"}, ] diff --git a/yuthon_report/models/month_receivable_report.py b/yuthon_report/models/month_receivable_report.py index d6ab5cd37..5c8455ea5 100644 --- a/yuthon_report/models/month_receivable_report.py +++ b/yuthon_report/models/month_receivable_report.py @@ -18,6 +18,7 @@ class MonthReceivableReport(models.Model): """ # 解析查询月 query_date = datetime.datetime.fromisoformat(params['date_start']).date() if params.get('date_start') else datetime.date.today().replace(day=1) + summary_only = params.get("summary_only", False) month_first = query_date.replace(day=1) last_day = calendar.monthrange(query_date.year, query_date.month)[1] month_last = query_date.replace(day=last_day) @@ -81,6 +82,11 @@ class MonthReceivableReport(models.Model): rec_map[rid] = {'rec': line.receivable_id, 'amount': 0.0} rec_map[rid]['amount'] += line.amount or 0.0 + if summary_only: + total_amount = sum(round(data['amount'], 2) for data in rec_map.values()) + stats = [{'name': '合计', 'amount': round(total_amount, 2)}] + return {'rows': [], 'stats': stats} + rows = [] total_amount = 0.0 for idx, (rid, data) in enumerate(rec_map.items(), start=1): diff --git a/yuthon_report/models/monthly_precollection_report.py b/yuthon_report/models/monthly_precollection_report.py index c95bc1fee..d02ec33b4 100644 --- a/yuthon_report/models/monthly_precollection_report.py +++ b/yuthon_report/models/monthly_precollection_report.py @@ -19,6 +19,7 @@ class MonthlyPrecollectionReport(models.Model): today = datetime.date.today() date_start = params.get("date_start") + summary_only = params.get("summary_only", False) query_date = datetime.datetime.fromisoformat(date_start).date() if date_start else today # 查询月月初和月末 @@ -102,6 +103,14 @@ class MonthlyPrecollectionReport(models.Model): rec_map[rid] = {'rec': line.receivable_id, 'amount': 0.0} rec_map[rid]['amount'] += line.amount or 0.0 + if summary_only: + total_amount = sum(round(data['amount'], 2) for data in rec_map.values()) + stats = [ + {'name': '记录总数', 'amount': len(rec_map)}, + {'name': '预收总额', 'amount': round(total_amount, 2)}, + ] + return {"rows": [], "stats": stats} + rows = [] total_amount = 0.0 for idx, (rid, data) in enumerate(rec_map.items(), start=1): diff --git a/yuthon_report/models/parking_fee_current_month_receivable.py b/yuthon_report/models/parking_fee_current_month_receivable.py index 9a834e387..fb737e261 100644 --- a/yuthon_report/models/parking_fee_current_month_receivable.py +++ b/yuthon_report/models/parking_fee_current_month_receivable.py @@ -18,6 +18,8 @@ class ParkingFeeCurrentMonthReceivable(models.Model): # 关键词 keyword = params.get("keyword") + # summary_only:看板只取 stats 不构建 rows + summary_only = params.get("summary_only", False) if keyword: domain += [ '|', '|', @@ -71,6 +73,10 @@ class ParkingFeeCurrentMonthReceivable(models.Model): rows = [] total_amount = 0.0 + # summary_only:跳过 rows 构建,直接算 stats + if summary_only: + total_amount = sum(rec.original_amount or 0.0 for rec in receivables) + return {"rows": [], "stats": [{"name": "合计", "amount": round(total_amount, 2)}]} for idx, rec in enumerate(receivables, start=1): amount = rec.original_amount or 0.0 total_amount += amount diff --git a/yuthon_report/models/parking_fee_current_month_report.py b/yuthon_report/models/parking_fee_current_month_report.py index 78e4bd052..e4a82d17c 100644 --- a/yuthon_report/models/parking_fee_current_month_report.py +++ b/yuthon_report/models/parking_fee_current_month_report.py @@ -17,6 +17,8 @@ class ParkingFeeCurrentMonthReport(models.Model): domain = [] keyword = params.get("keyword") + # summary_only:看板只取 stats 不构建 rows + summary_only = params.get("summary_only", False) if keyword: domain += [ '|', '|', @@ -77,6 +79,10 @@ class ParkingFeeCurrentMonthReport(models.Model): rows = [] total_amount = 0.0 + # summary_only:跳过 rows 构建,直接算 stats + if summary_only: + total_amount = sum(rec.delivered1 or 0.0 for rec in receivables) + return {"rows": [], "stats": [{"name": "合计", "amount": round(total_amount, 2)}]} for idx, rec in enumerate(receivables, start=1): amount = rec.delivered1 or 0.0 total_amount += amount diff --git a/yuthon_report/models/parking_fee_yearly_report.py b/yuthon_report/models/parking_fee_yearly_report.py index 638b86f5f..da3389de1 100644 --- a/yuthon_report/models/parking_fee_yearly_report.py +++ b/yuthon_report/models/parking_fee_yearly_report.py @@ -18,6 +18,8 @@ class ParkingFeeYearlyReport(models.Model): # 关键词 keyword = params.get("keyword") + # summary_only:看板只取 stats 不构建 rows + summary_only = params.get("summary_only", False) if keyword: domain += [ '|', '|', @@ -82,6 +84,10 @@ class ParkingFeeYearlyReport(models.Model): rows = [] total_amount = 0.0 + # summary_only:跳过 rows 构建,直接算 stats + if summary_only: + total_amount = sum(rec.original_amount or 0.0 for rec in receivables) + return {"rows": [], "stats": [{"name": "合计", "amount": round(total_amount, 2)}]} for idx, rec in enumerate(receivables, start=1): amount = rec.original_amount or 0.0 total_amount += amount diff --git a/yuthon_report/models/penalty_arrears_report.py b/yuthon_report/models/penalty_arrears_report.py index 982d143e5..43b2f5a4c 100644 --- a/yuthon_report/models/penalty_arrears_report.py +++ b/yuthon_report/models/penalty_arrears_report.py @@ -34,6 +34,7 @@ class PenaltyArrearsReport(models.Model): # ========== 一、参数解析 ========== date_end = params.get("date_end") # 查询截止日期(如 "2026-01-01 23:59:59") + summary_only = params.get("summary_only", False) query_date = None # 查询日期 date 对象,初始化为空 if date_end: # 如果有截止日期参数 query_date = datetime.datetime.fromisoformat(date_end).date() # 将字符串转为 date 对象 @@ -169,7 +170,8 @@ class PenaltyArrearsReport(models.Model): total_uncollected += penalty_uncollected_amt contract = penalty_rec.contract_id - rows.append({ + if not summary_only: + rows.append({ "来源": "A", "租金ID": rent_rec.id if rent_rec else "", "租金应收日": rent_due_date_str, @@ -230,6 +232,21 @@ class PenaltyArrearsReport(models.Model): # 对这些租金记录,动态计算违约金总额,判断是否有未收 # 先查出涉及的租金记录 rent_ids_b = list(rent_penalty_collected.keys()) + # ========== 优化:批量查询所有租金对应的非违约金收款行,消除N+1 ========== + all_modify_lines = self.env['receivable.modify.line'].sudo().search([ + ('receivable_id', 'in', rent_ids_b), + ('reconciliation_date', '!=', False), + ('is_penalty', '=', False), + ], order='receivable_id, reconciliation_date asc') + # 按 receivable_id 分组 + modify_lines_by_rent = {} + for ml in all_modify_lines: + rid = ml.receivable_id.id + if rid not in modify_lines_by_rent: + modify_lines_by_rent[rid] = [] + modify_lines_by_rent[rid].append(ml) + # ====================================================================== + rent_recs_b = self.sudo().browse(rent_ids_b) if rent_ids_b else self.sudo().browse([]) for rent_rec in rent_recs_b: @@ -264,11 +281,8 @@ class PenaltyArrearsReport(models.Model): penalty_rate = _get_penalty_rate(rent_rec.contract_id) # --- 获取该租金的支付历史(非违约金的 modify lines) --- - modify_lines = self.env['receivable.modify.line'].sudo().search([ - ('receivable_id', '=', rent_rec.id), - ('reconciliation_date', '!=', False), - ('is_penalty', '=', False), # 非违约金收款行 - ], order='reconciliation_date asc') + # 从批量预分组字典中取,消除N+1(第233-245行已批量查询并分组) + modify_lines = modify_lines_by_rent.get(rent_rec.id, []) # --- 分段计算违约金总额 --- rent_amount = (rent_rec.original_amount or 0.0) - (rent_rec.discount_amount or 0.0) @@ -343,8 +357,9 @@ class PenaltyArrearsReport(models.Model): total_uncollected += penalty_uncollected_amt contract = rent_rec.contract_id - rows.append({ - "来源": "B", + if not summary_only: + rows.append({ + "来源": "B", "租金ID": rent_rec.id, "租金应收日": due_date.strftime("%Y-%m-%d"), "租金收款日期": rent_rec.payout_date.strftime("%Y-%m-%d") if rent_rec.payout_date else "", @@ -475,8 +490,9 @@ class PenaltyArrearsReport(models.Model): total_uncollected += penalty_uncollected_amt contract = rent_rec.contract_id - rows.append({ - "来源": "C", + if not summary_only: + rows.append({ + "来源": "C", "租金ID": rent_rec.id, "租金应收日": due_date.strftime("%Y-%m-%d"), "租金收款日期": rent_rec.payout_date.strftime("%Y-%m-%d") if rent_rec.payout_date else "", diff --git a/yuthon_report/models/penalty_month_received_report.py b/yuthon_report/models/penalty_month_received_report.py index c1e1acc13..5e9915009 100644 --- a/yuthon_report/models/penalty_month_received_report.py +++ b/yuthon_report/models/penalty_month_received_report.py @@ -29,6 +29,7 @@ class PenaltyMonthReceivedReport(models.Model): # ========== 一、参数解析 ========== date_start = params.get("date_start") # 查询月开始日期 date_end = params.get("date_end") # 查询月结束日期 + summary_only = params.get("summary_only", False) # 没有时间范围则无数据 if not date_start or not date_end: @@ -205,38 +206,39 @@ class PenaltyMonthReceivedReport(models.Model): total_collected += pay_amount contract = penalty_rec.contract_id - rows.append({ - "来源": "A", - "租金ID": rent_rec.id if rent_rec else "", - "租金应收日": rent_due_date_str, - "租金收款日期": rent_payout_date_str, - "租金金额": rent_amount_val, - "租金已收": rent_collected_amt, - "租金未收": rent_uncollected_amt, - "租金状态": rent_status, - "违约金金额": penalty_amount, - "违约金已收": penalty_collected_amt, - "违约金未收": penalty_uncollected_amt, - "违约金状态": penalty_status, - "违约金应收开始": penalty_rec.due_date.strftime("%Y-%m-%d") if penalty_rec.due_date else "", - "实收金额": round(pay_amount, 2), - "收款日期": ml.reconciliation_date.strftime("%Y-%m-%d") if ml.reconciliation_date else "", - "收款备注": ml.remake1 or "", - "收费区间": penalty_rec.charging_range or "", - "备注": penalty_rec.remark or "", - "合同编号": { - 'model': 'property.lease.contract', - 'id': contract.id, - 'display_name': contract.contract_code, - } if contract else "", - "物业名称": _build_property_display(contract, prop_name), - "承租方名称": { - 'model': 'property.tenant.information', - 'id': penalty_rec.lessee_id.id, - 'display_name': penalty_rec.lessee_id.name, - } if penalty_rec.lessee_id else "", - "所属公司": penalty_rec.company_id.name if penalty_rec.company_id else "", - }) + if not summary_only: + rows.append({ + "来源": "A", + "租金ID": rent_rec.id if rent_rec else "", + "租金应收日": rent_due_date_str, + "租金收款日期": rent_payout_date_str, + "租金金额": rent_amount_val, + "租金已收": rent_collected_amt, + "租金未收": rent_uncollected_amt, + "租金状态": rent_status, + "违约金金额": penalty_amount, + "违约金已收": penalty_collected_amt, + "违约金未收": penalty_uncollected_amt, + "违约金状态": penalty_status, + "违约金应收开始": penalty_rec.due_date.strftime("%Y-%m-%d") if penalty_rec.due_date else "", + "实收金额": round(pay_amount, 2), + "收款日期": ml.reconciliation_date.strftime("%Y-%m-%d") if ml.reconciliation_date else "", + "收款备注": ml.remake1 or "", + "收费区间": penalty_rec.charging_range or "", + "备注": penalty_rec.remark or "", + "合同编号": { + 'model': 'property.lease.contract', + 'id': contract.id, + 'display_name': contract.contract_code, + } if contract else "", + "物业名称": _build_property_display(contract, prop_name), + "承租方名称": { + 'model': 'property.tenant.information', + 'id': penalty_rec.lessee_id.id, + 'display_name': penalty_rec.lessee_id.name, + } if penalty_rec.lessee_id else "", + "所属公司": penalty_rec.company_id.name if penalty_rec.company_id else "", + }) # ========== 三、来源B:receivable.modify.line 中 is_penalty=True 的记录 ========== # 核心关系:违约金的 due_date(应收开始)= 关联租金的 payout_date(收款日期) @@ -252,6 +254,36 @@ class PenaltyMonthReceivedReport(models.Model): ] penalty_modify_lines = self.env['receivable.modify.line'].sudo().search(source_b_domain) + # ========== 优化:批量查询所有潜在匹配的租金,消除N+1 ========== + # 收集所有需要的 penalty_due 日期 + penalty_due_set = set() + for pml in penalty_modify_lines: + penalty_rec = pml.receivable_id + if not penalty_rec or not penalty_rec.due_date: + continue + penalty_due = penalty_rec.due_date.date() if hasattr(penalty_rec.due_date, 'date') else penalty_rec.due_date + penalty_due_set.add(penalty_due) + + # 批量查询所有潜在匹配的租金 + if penalty_due_set: + batch_rent_domain = [('payout_date', 'in', list(penalty_due_set))] + batch_rent_recs = self.sudo().search(batch_rent_domain) + + # 按 (payout_date, lessee_id.id, contract_id.id) 分组 + rent_by_key = {} + for rent in batch_rent_recs: + key = ( + rent.payout_date.date() if hasattr(rent.payout_date, 'date') else rent.payout_date, + rent.lessee_id.id if rent.lessee_id else None, + rent.contract_id.id if rent.contract_id else None, + ) + if key not in rent_by_key: + rent_by_key[key] = [] + rent_by_key[key].append(rent) + else: + rent_by_key = {} + # ===================================================================== + for pml in penalty_modify_lines: # 取违约金金额(delivered1 字段) pay_amount = round(pml.delivered1 or 0.0, 2) @@ -273,17 +305,13 @@ class PenaltyMonthReceivedReport(models.Model): # 统一转为 date 对象用于搜索 penalty_due = penalty_due_date.date() if hasattr(penalty_due_date, 'date') else penalty_due_date - # 在 property.accounts.receivable 中查找关联租金 - # 条件:payout_date = 违约金的 due_date + 同承租方 + 同合同 - rent_domain = [ - ('payout_date', '=', penalty_due), # 租金收款日期 = 违约金应收开始日期 - ] - if pml.lessee_id: - rent_domain.append(('lessee_id', '=', pml.lessee_id.id)) # 同一承租方 - if pml.contract_id: - rent_domain.append(('contract_id', '=', pml.contract_id.id)) # 同一合同 - - rent_recs = self.sudo().search(rent_domain) + # 从批量预分组字典中查找匹配租金,消除N+1(第262-289行已批量查询并分组) + key = ( + penalty_due, + pml.lessee_id.id if pml.lessee_id else None, + pml.contract_id.id if pml.contract_id else None, + ) + rent_recs = rent_by_key.get(key, []) if not rent_recs: continue @@ -373,38 +401,39 @@ class PenaltyMonthReceivedReport(models.Model): lessee_obj = penalty_rec.lessee_id or pml.lessee_id company_obj = penalty_rec.company_id or pml.company_id - rows.append({ - "来源": "B", - "租金ID": rent_receivable.id if rent_receivable else "", - "租金应收日": rent_due_date_str, - "租金收款日期": rent_payout_date_str, - "租金金额": rent_amount_val, - "租金已收": rent_collected_amt, - "租金未收": rent_uncollected_amt, - "租金状态": rent_status, - "违约金金额": penalty_amount, - "违约金已收": penalty_collected_amt, - "违约金未收": penalty_uncollected_amt, - "违约金状态": penalty_status, - "违约金应收开始": penalty_rec.due_date.strftime("%Y-%m-%d") if penalty_rec.due_date else "", - "实收金额": pay_amount, - "收款日期": pml.reconciliation_date.strftime("%Y-%m-%d") if pml.reconciliation_date else "", - "收款备注": pml.remake1 or "", - "收费区间": charging_range, - "备注": penalty_rec.remark or "", - "合同编号": { - 'model': 'property.lease.contract', - 'id': contract.id, - 'display_name': contract.contract_code, - } if contract else "", - "物业名称": _build_property_display(contract, prop_name), - "承租方名称": { - 'model': 'property.tenant.information', - 'id': lessee_obj.id, - 'display_name': lessee_obj.name, - } if lessee_obj else "", - "所属公司": company_obj.name if company_obj else "", - }) + if not summary_only: + rows.append({ + "来源": "B", + "租金ID": rent_receivable.id if rent_receivable else "", + "租金应收日": rent_due_date_str, + "租金收款日期": rent_payout_date_str, + "租金金额": rent_amount_val, + "租金已收": rent_collected_amt, + "租金未收": rent_uncollected_amt, + "租金状态": rent_status, + "违约金金额": penalty_amount, + "违约金已收": penalty_collected_amt, + "违约金未收": penalty_uncollected_amt, + "违约金状态": penalty_status, + "违约金应收开始": penalty_rec.due_date.strftime("%Y-%m-%d") if penalty_rec.due_date else "", + "实收金额": pay_amount, + "收款日期": pml.reconciliation_date.strftime("%Y-%m-%d") if pml.reconciliation_date else "", + "收款备注": pml.remake1 or "", + "收费区间": charging_range, + "备注": penalty_rec.remark or "", + "合同编号": { + 'model': 'property.lease.contract', + 'id': contract.id, + 'display_name': contract.contract_code, + } if contract else "", + "物业名称": _build_property_display(contract, prop_name), + "承租方名称": { + 'model': 'property.tenant.information', + 'id': lessee_obj.id, + 'display_name': lessee_obj.name, + } if lessee_obj else "", + "所属公司": company_obj.name if company_obj else "", + }) # ========== 四、构建统计汇总数据 ========== stats = [ diff --git a/yuthon_report/models/penalty_year_cum_report.py b/yuthon_report/models/penalty_year_cum_report.py index f8b7ecdfa..4fc19c3b5 100644 --- a/yuthon_report/models/penalty_year_cum_report.py +++ b/yuthon_report/models/penalty_year_cum_report.py @@ -29,6 +29,7 @@ class PenaltyYearCumReport(models.Model): # ========== 一、参数解析 ========== date_start = params.get("date_start") date_end = params.get("date_end") + summary_only = params.get("summary_only", False) # 没有时间范围则无数据 if not date_start or not date_end: @@ -207,38 +208,39 @@ class PenaltyYearCumReport(models.Model): total_collected += pay_amount contract = penalty_rec.contract_id - rows.append({ - "来源": "A", - "租金ID": rent_rec.id if rent_rec else "", - "租金应收日": rent_due_date_str, - "租金收款日期": rent_payout_date_str, - "租金金额": rent_amount_val, - "租金已收": rent_collected_amt, - "租金未收": rent_uncollected_amt, - "租金状态": rent_status, - "违约金金额": penalty_amount, - "违约金已收": penalty_collected_amt, - "违约金未收": penalty_uncollected_amt, - "违约金状态": penalty_status, - "违约金应收开始": penalty_rec.due_date.strftime("%Y-%m-%d") if penalty_rec.due_date else "", - "实收金额": round(pay_amount, 2), - "收款日期": ml.reconciliation_date.strftime("%Y-%m-%d") if ml.reconciliation_date else "", - "收款备注": ml.remake1 or "", - "收费区间": penalty_rec.charging_range or "", - "备注": penalty_rec.remark or "", - "合同编号": { - 'model': 'property.lease.contract', - 'id': contract.id, - 'display_name': contract.contract_code, - } if contract else "", - "物业名称": _build_property_display(contract, prop_name), - "承租方名称": { - 'model': 'property.tenant.information', - 'id': penalty_rec.lessee_id.id, - 'display_name': penalty_rec.lessee_id.name, - } if penalty_rec.lessee_id else "", - "所属公司": penalty_rec.company_id.name if penalty_rec.company_id else "", - }) + if not summary_only: + rows.append({ + "来源": "A", + "租金ID": rent_rec.id if rent_rec else "", + "租金应收日": rent_due_date_str, + "租金收款日期": rent_payout_date_str, + "租金金额": rent_amount_val, + "租金已收": rent_collected_amt, + "租金未收": rent_uncollected_amt, + "租金状态": rent_status, + "违约金金额": penalty_amount, + "违约金已收": penalty_collected_amt, + "违约金未收": penalty_uncollected_amt, + "违约金状态": penalty_status, + "违约金应收开始": penalty_rec.due_date.strftime("%Y-%m-%d") if penalty_rec.due_date else "", + "实收金额": round(pay_amount, 2), + "收款日期": ml.reconciliation_date.strftime("%Y-%m-%d") if ml.reconciliation_date else "", + "收款备注": ml.remake1 or "", + "收费区间": penalty_rec.charging_range or "", + "备注": penalty_rec.remark or "", + "合同编号": { + 'model': 'property.lease.contract', + 'id': contract.id, + 'display_name': contract.contract_code, + } if contract else "", + "物业名称": _build_property_display(contract, prop_name), + "承租方名称": { + 'model': 'property.tenant.information', + 'id': penalty_rec.lessee_id.id, + 'display_name': penalty_rec.lessee_id.name, + } if penalty_rec.lessee_id else "", + "所属公司": penalty_rec.company_id.name if penalty_rec.company_id else "", + }) # ========== 三、来源B:receivable.modify.line 中 is_penalty=True 的记录 ========== # 核心关系:违约金的 due_date(应收开始)= 关联租金的 payout_date(收款日期) @@ -254,6 +256,36 @@ class PenaltyYearCumReport(models.Model): ] penalty_modify_lines = self.env['receivable.modify.line'].sudo().search(source_b_domain) + # ========== 优化:批量查询所有潜在匹配的租金,消除N+1 ========== + # 收集所有需要的 penalty_due 日期 + penalty_due_set = set() + for pml in penalty_modify_lines: + penalty_rec = pml.receivable_id + if not penalty_rec or not penalty_rec.due_date: + continue + penalty_due = penalty_rec.due_date.date() if hasattr(penalty_rec.due_date, 'date') else penalty_rec.due_date + penalty_due_set.add(penalty_due) + + # 批量查询所有潜在匹配的租金 + if penalty_due_set: + batch_rent_domain = [('payout_date', 'in', list(penalty_due_set))] + batch_rent_recs = self.sudo().search(batch_rent_domain) + + # 按 (payout_date, lessee_id.id, contract_id.id) 分组 + rent_by_key = {} + for rent in batch_rent_recs: + key = ( + rent.payout_date.date() if hasattr(rent.payout_date, 'date') else rent.payout_date, + rent.lessee_id.id if rent.lessee_id else None, + rent.contract_id.id if rent.contract_id else None, + ) + if key not in rent_by_key: + rent_by_key[key] = [] + rent_by_key[key].append(rent) + else: + rent_by_key = {} + # ===================================================================== + for pml in penalty_modify_lines: # 取违约金金额(delivered1 字段) pay_amount = round(pml.delivered1 or 0.0, 2) @@ -275,17 +307,13 @@ class PenaltyYearCumReport(models.Model): # 统一转为 date 对象用于搜索 penalty_due = penalty_due_date.date() if hasattr(penalty_due_date, 'date') else penalty_due_date - # 在 property.accounts.receivable 中查找关联租金 - # 条件:payout_date = 违约金的 due_date + 同承租方 + 同合同 - rent_domain = [ - ('payout_date', '=', penalty_due), # 租金收款日期 = 违约金应收开始日期 - ] - if pml.lessee_id: - rent_domain.append(('lessee_id', '=', pml.lessee_id.id)) # 同一承租方 - if pml.contract_id: - rent_domain.append(('contract_id', '=', pml.contract_id.id)) # 同一合同 - - rent_recs = self.sudo().search(rent_domain) + # 从批量预分组字典中查找匹配租金,消除N+1(第262-289行已批量查询并分组) + key = ( + penalty_due, + pml.lessee_id.id if pml.lessee_id else None, + pml.contract_id.id if pml.contract_id else None, + ) + rent_recs = rent_by_key.get(key, []) if not rent_recs: continue @@ -374,38 +402,39 @@ class PenaltyYearCumReport(models.Model): lessee_obj = penalty_rec.lessee_id or pml.lessee_id company_obj = penalty_rec.company_id or pml.company_id - rows.append({ - "来源": "B", - "租金ID": rent_receivable.id if rent_receivable else "", - "租金应收日": rent_due_date_str, - "租金收款日期": rent_payout_date_str, - "租金金额": rent_amount_val, - "租金已收": rent_collected_amt, - "租金未收": rent_uncollected_amt, - "租金状态": rent_status, - "违约金金额": penalty_amount, - "违约金已收": penalty_collected_amt, - "违约金未收": penalty_uncollected_amt, - "违约金状态": penalty_status, - "违约金应收开始": penalty_rec.due_date.strftime("%Y-%m-%d") if penalty_rec.due_date else "", - "实收金额": pay_amount, - "收款日期": pml.reconciliation_date.strftime("%Y-%m-%d") if pml.reconciliation_date else "", - "收款备注": pml.remake1 or "", - "收费区间": charging_range, - "备注": penalty_rec.remark or "", - "合同编号": { - 'model': 'property.lease.contract', - 'id': contract.id, - 'display_name': contract.contract_code, - } if contract else "", - "物业名称": _build_property_display(contract, prop_name), - "承租方名称": { - 'model': 'property.tenant.information', - 'id': lessee_obj.id, - 'display_name': lessee_obj.name, - } if lessee_obj else "", - "所属公司": company_obj.name if company_obj else "", - }) + if not summary_only: + rows.append({ + "来源": "B", + "租金ID": rent_receivable.id if rent_receivable else "", + "租金应收日": rent_due_date_str, + "租金收款日期": rent_payout_date_str, + "租金金额": rent_amount_val, + "租金已收": rent_collected_amt, + "租金未收": rent_uncollected_amt, + "租金状态": rent_status, + "违约金金额": penalty_amount, + "违约金已收": penalty_collected_amt, + "违约金未收": penalty_uncollected_amt, + "违约金状态": penalty_status, + "违约金应收开始": penalty_rec.due_date.strftime("%Y-%m-%d") if penalty_rec.due_date else "", + "实收金额": pay_amount, + "收款日期": pml.reconciliation_date.strftime("%Y-%m-%d") if pml.reconciliation_date else "", + "收款备注": pml.remake1 or "", + "收费区间": charging_range, + "备注": penalty_rec.remark or "", + "合同编号": { + 'model': 'property.lease.contract', + 'id': contract.id, + 'display_name': contract.contract_code, + } if contract else "", + "物业名称": _build_property_display(contract, prop_name), + "承租方名称": { + 'model': 'property.tenant.information', + 'id': lessee_obj.id, + 'display_name': lessee_obj.name, + } if lessee_obj else "", + "所属公司": company_obj.name if company_obj else "", + }) # ========== 四、构建统计汇总数据 ========== stats = [ diff --git a/yuthon_report/models/pre_advance_report.py b/yuthon_report/models/pre_advance_report.py index f6e38963f..b051ee6c8 100644 --- a/yuthon_report/models/pre_advance_report.py +++ b/yuthon_report/models/pre_advance_report.py @@ -24,6 +24,7 @@ class PreAdvanceReport(models.Model): this_month_last = next_month_first - datetime.timedelta(days=1) date_start = params.get("date_start") + summary_only = params.get("summary_only", False) # 应收日期在查询日当月及之后(即未来应收) cutoff_date = datetime.datetime.fromisoformat(date_start).date() if date_start else this_month_last # 取查询日所在月的第一天,应收日期 >= 该月第一天 @@ -123,6 +124,14 @@ class PreAdvanceReport(models.Model): if rec_map[rid]['earliest_pay_date'] is None or pay_date_val < rec_map[rid]['earliest_pay_date']: rec_map[rid]['earliest_pay_date'] = pay_date_val + if summary_only: + total_amount = sum(round(data['amount'], 2) for data in rec_map.values()) + stats = [ + {'name': '记录总数', 'amount': len(rec_map)}, + {'name': '预收总额', 'amount': round(total_amount, 2)}, + ] + return {"rows": [], "stats": stats} + rows = [] total_amount = 0.0 for idx, (rid, data) in enumerate(rec_map.items(), start=1): diff --git a/yuthon_report/models/pre_arrears_report.py b/yuthon_report/models/pre_arrears_report.py index 049977c7d..5f65ba133 100644 --- a/yuthon_report/models/pre_arrears_report.py +++ b/yuthon_report/models/pre_arrears_report.py @@ -30,6 +30,7 @@ class PreArrearsReport(models.Model): date_start = params.get("date_start") date_end = params.get("date_end") + summary_only = params.get("summary_only", False) # 应收日期在查询月及之前(包含查询月) cutoff_date = datetime.datetime.fromisoformat(date_end).date() if date_end else this_month_first # 查询月范围(用于判断收款是否在查询月内) @@ -121,9 +122,13 @@ class PreArrearsReport(models.Model): covered_ids = list(rec_map.keys()) # 找出所有在 modify.line 中有记录的 receivable_id(不管 payout_date 是什么时候) - all_line_receivable_ids = set( - self.sudo().search([('receivable_id', '!=', False)]).mapped('receivable_id.id') + # 优化:用 read_group 只取 distinct receivable_id,避免全表加载到内存 + all_line_receivable_groups = self.sudo().read_group( + [('receivable_id', '!=', False)], + ['receivable_id'], + ['receivable_id'], ) + all_line_receivable_ids = set(g['receivable_id'][0] for g in all_line_receivable_groups if g['receivable_id']) # Part 2 只查真正"从未对过账"的 receivable exclude_ids = all_line_receivable_ids # 包含 covered_ids 和 payout_date 被过滤掉的 @@ -178,6 +183,13 @@ class PreArrearsReport(models.Model): # ============================================================ rows = [] total_amount = 0.0 + if summary_only: + total_amount = sum(round(data['amount'], 2) for data in rec_map.values()) + stats = [ + {'name': '记录总数', 'amount': len(rec_map)}, + {'name': '欠收总额', 'amount': round(total_amount, 2)}, + ] + return {"rows": [], "stats": stats} for idx, (rid, data) in enumerate(rec_map.items(), start=1): rec = data['rec'] contract = rec.contract_id diff --git a/yuthon_report/models/receivable_report.py b/yuthon_report/models/receivable_report.py index 3851c1035..bf915c926 100644 --- a/yuthon_report/models/receivable_report.py +++ b/yuthon_report/models/receivable_report.py @@ -44,6 +44,7 @@ class YuthonProperty(models.Model): @api.model def get_complete_ledger_report(self, params): + summary_only = params.get("summary_only", False) domain = [('is_invalid', '!=', True)] if params.get("keyword"): keyword = params["keyword"] @@ -90,6 +91,20 @@ class YuthonProperty(models.Model): total_subtotal = 0.0 idx = 1 + if summary_only: + for cid, data in contract_map.items(): + for rec in data['recs']: + type_name = rec.management_type_id.name if rec.management_type_id else '' + col = self._classify_fee_type(type_name) + total_fees[col] += (rec.original_amount or 0.0) - (rec.discount_amount or 0.0) + subtotal = sum((rec.original_amount or 0.0) - (rec.discount_amount or 0.0) for rec in data['recs']) + total_subtotal += subtotal + summary_stats = [] + for col in self.FEE_COLUMNS: + summary_stats.append({'name': col, 'amount': round(total_fees[col], 2)}) + summary_stats.append({'name': '小计合计', 'amount': round(total_subtotal, 2)}) + return {"rows": [], "stats": summary_stats} + for cid, data in contract_map.items(): info_rec = data['info_rec'] fee_row = {col: 0.0 for col in self.FEE_COLUMNS} diff --git a/yuthon_report/models/rent_area_report.py b/yuthon_report/models/rent_area_report.py index d3cdc182a..753198181 100644 --- a/yuthon_report/models/rent_area_report.py +++ b/yuthon_report/models/rent_area_report.py @@ -39,6 +39,7 @@ class RentAreaReport(models.Model): property_id = params.get('property_id') lessee_id = params.get('lessee_id') contract_id = params.get('contract_id') + summary_only = params.get("summary_only", False) # ========== 二、查询日期解析 ========== query_date = None @@ -124,6 +125,7 @@ class RentAreaReport(models.Model): # ========== 六、构建报表行 ========== rows = [] + row_count = 0 total_rent_area = 0.0 seen_property_ids = set() # 已计入合计的物业id集合 @@ -215,33 +217,35 @@ class RentAreaReport(models.Model): seen_property_ids.add(property_rec.id) # --- 6.6 构建行数据(物业名称、合同编号、承租方支持穿透) --- - rows.append({ - "序号": idx, - "物业id": property_rec.id, - "物业名称": { - 'model': 'yuthon.property', - 'id': property_rec.id, - 'display_name': property_rec.name or "", - } if property_rec.name else "", - "合同编号": { - 'model': 'property.lease.contract', - 'id': contract_record_id, - 'display_name': contract_code, - } if contract_record_id and "、" not in contract_code else contract_code or "", - "承租方": { - 'model': 'property.tenant.information', - 'id': lessee_record_id, - 'display_name': lessee_name, - } if lessee_record_id and "、" not in lessee_name else lessee_name or "", - "产权证号": title_certificate, - "产权地址": property_address, - "可租面积": round(rent_area, 4), - "片区管理员": admin_display, - }) + if not summary_only: + rows.append({ + "序号": idx, + "物业id": property_rec.id, + "物业名称": { + 'model': 'yuthon.property', + 'id': property_rec.id, + 'display_name': property_rec.name or "", + } if property_rec.name else "", + "合同编号": { + 'model': 'property.lease.contract', + 'id': contract_record_id, + 'display_name': contract_code, + } if contract_record_id and "、" not in contract_code else contract_code or "", + "承租方": { + 'model': 'property.tenant.information', + 'id': lessee_record_id, + 'display_name': lessee_name, + } if lessee_record_id and "、" not in lessee_name else lessee_name or "", + "产权证号": title_certificate, + "产权地址": property_address, + "可租面积": round(rent_area, 4), + "片区管理员": admin_display, + }) + row_count += 1 # ========== 七、构建统计汇总数据 ========== stats = [ - {'name': '记录总数', 'amount': len(rows)}, + {'name': '记录总数', 'amount': row_count}, {'name': '可租面积合计', 'amount': round(total_rent_area, 4), 'display_amount': f"{round(total_rent_area, 4):.4f}"}, ] diff --git a/yuthon_report/models/total_received_report.py b/yuthon_report/models/total_received_report.py index e263177b3..38b9f38df 100644 --- a/yuthon_report/models/total_received_report.py +++ b/yuthon_report/models/total_received_report.py @@ -12,6 +12,7 @@ class TotalReceivedReport(models.Model): """本月实收总额 = 归属本月实收 + 本月预收 取数逻辑:收款日期(reconciliation_date)在查询月内的所有收款记录,金额取 amount """ + summary_only = params.get("summary_only", False) domain = [ ('reconciliation_date', '!=', False), ('receivable_id.is_invalid', '!=', True), @@ -61,6 +62,11 @@ class TotalReceivedReport(models.Model): lines = self.search(domain, order='reconciliation_date asc') + if summary_only: + total_amount = sum(round(line.amount or 0.0, 2) for line in lines) + stats = [{'name': '合计', 'amount': round(total_amount, 2)}] + return {'rows': [], 'stats': stats} + rows = [] total_amount = 0.0 for idx, line in enumerate(lines, start=1): diff --git a/yuthon_report/models/yuthon_dashboard.py b/yuthon_report/models/yuthon_dashboard.py index 961db0510..9601ba4de 100644 --- a/yuthon_report/models/yuthon_dashboard.py +++ b/yuthon_report/models/yuthon_dashboard.py @@ -53,11 +53,13 @@ class YuthonDashboard(models.Model): def safe_get(model_name, method_name, stats_index=1, overrides=None): """安全调用子报表方法,返回汇总金额 :param overrides: 可选 dict,覆盖 base_params 中的参数 + 看板只取 stats 不构建 rows,传 summary_only=True 减少遍历开销 """ try: params = dict(base_params) if overrides: params.update(overrides) + params['summary_only'] = True # 看板只取汇总,跳过 rows 构建 result = getattr(self.env[model_name], method_name)(params) stats = result.get('stats', []) if stats and len(stats) > stats_index: @@ -427,18 +429,20 @@ class YuthonDashboard(models.Model): 已租面积拆为两个卡片:物业已租面积、合同已租面积 """ try: - # 管理面积:调用管理面积报表 - management_result = self.env['management.area.report'].get_management_area_report(base_params) + # 管理面积:调用管理面积报表(summary_only=True 跳过 rows 构建) + area_params = dict(base_params) + area_params['summary_only'] = True + management_result = self.env['management.area.report'].get_management_area_report(area_params) management_stats = management_result.get('stats', []) total_management = management_stats[1].get('amount', 0.0) if len(management_stats) > 1 else 0.0 # 可租面积:调用可租面积报表 - rent_result = self.env['rent.area.report'].get_rent_area_report(base_params) + rent_result = self.env['rent.area.report'].get_rent_area_report(area_params) rent_stats = rent_result.get('stats', []) total_rent = rent_stats[1].get('amount', 0.0) if len(rent_stats) > 1 else 0.0 # 已租面积:调用已租面积报表(返回物业已租面积和合同已租面积两个汇总) - leased_result = self.env['leased.area.report'].get_leased_area_report(base_params) + leased_result = self.env['leased.area.report'].get_leased_area_report(area_params) leased_stats = leased_result.get('stats', []) # stats[1] = 物业已租面积合计, stats[2] = 合同已租面积合计 total_property_leased = leased_stats[1].get('amount', 0.0) if len(leased_stats) > 1 else 0.0 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 cbe644870..8b101fc11 100644 --- a/yuthon_report/static/src/components/yuthon_dashboard/yuthon_dashboard.js +++ b/yuthon_report/static/src/components/yuthon_dashboard/yuthon_dashboard.js @@ -52,10 +52,8 @@ class YuthonDashboard extends Component { } catch (e) { /* ignore */ } onMounted(() => { - // 如果已从缓存恢复数据,跳过重新查询 - if (!restored) { - this.onSearch(); - } + // 首次进入不自动查询,等用户选了查询条件后点击"查询"或切换筛选器时再查 + // 如果已从缓存恢复数据,直接展示即可 }); }