Files
gzth/yuthon_report/models/rent_area_report.py
T
2026-06-15 18:17:37 +08:00

252 lines
11 KiB
Python

import datetime
import logging
from odoo import api, models
_logger = logging.getLogger(__name__)
class RentAreaReport(models.Model):
_name = 'rent.area.report'
_description = '可租面积报表'
_auto = False
@api.model
def get_rent_area_report(self, params):
"""可租面积报表数据源。
核心逻辑:
1. 数据源为 manage.information.line(管理信息明细行)
2. 日期过滤:生效日期 <= 查询日期 <= 失效日期(失效日期为空表示至今有效)
3. 可租面积直接取 manage.information.line.rent_area
4. 产权证号、产权地址从 title.information.line 取(也按日期范围匹配)
5. 片区管理员取 precinct_id.display_name
6. 合同编号、承租方从 property.lease.contract.line 反查合同获取
7. 支持按物业名称(property_id)、承租方(lessee_id)、合同编号(contract_id)搜索
"""
# ========== 一、参数解析 ==========
date_start = params.get("date_start")
date_end = params.get("date_end")
# 单日期模式,优先取 date_start
query_date_str = date_start or date_end
keyword = params.get("keyword")
condition = params.get('condition')
partner_id = params.get('partner_id')
company_id = params.get('company_id')
# 前端侧边栏下拉选择器传入的 ID
property_id = params.get('property_id')
lessee_id = params.get('lessee_id')
contract_id = params.get('contract_id')
# ========== 二、查询日期解析 ==========
query_date = None
if query_date_str:
try:
# 截断时间后缀,如 "2024-11-01 00:00:00" -> "2024-11-01"
query_date = datetime.datetime.fromisoformat(query_date_str.split(' ')[0]).date()
except Exception:
_logger.warning("[RENT_AREA] 日期解析失败: %s", query_date_str)
# 没有查询日期时不做时间过滤(返回所有记录)
domain = [('yuthon_property_id', '!=', False)]
# 日期过滤:生效日期 <= 查询日期,且(失效日期 >= 查询日期 或 失效日期为空)
if query_date:
domain += [
('start_time', '<=', query_date),
'|', ('end_date', '>=', query_date), ('end_date', '=', False),
]
# 其他筛选条件
if condition:
domain.append(('tenure_type', '=', condition))
if partner_id:
domain.append(('precinct_id', '=', partner_id))
if company_id:
domain.append(('company_id', '=', company_id))
# 物业名称下拉选择器:直接过滤 yuthon_property_id
if property_id:
domain.append(('yuthon_property_id', '=', int(property_id)))
# ========== 三、查询管理信息明细行 ==========
manage_lines = self.env['manage.information.line'].sudo().search(domain)
# ========== 四、批量预取合同信息 ==========
# 避免每条记录单独查询,一次性查出所有物业对应的合同
# 关联路径:manage.information.line → yuthon_property_id → yuthon.property
# ← property.lease.contract.line(address_id) → lease_id → property.lease.contract
property_ids = list({line.yuthon_property_id.id for line in manage_lines if line.yuthon_property_id})
# 合同映射:property_id → 合同信息列表
contract_map = {}
if property_ids:
contract_lines = self.env['property.lease.contract.line'].sudo().search([
('address_id', 'in', property_ids),
])
for cl in contract_lines:
contract = cl.lease_id
if not contract:
continue
pid = cl.address_id.id
# 同一物业可能有多份合同,用列表存储
if pid not in contract_map:
contract_map[pid] = []
contract_map[pid].append({
'contract_id': contract.id,
'contract_code': contract.contract_code or "",
'lessee_id': contract.lessee_id.id if contract.lessee_id else False,
'lessee_name': contract.lessee_id.name if contract.lessee_id else "",
'actual_start_time': contract.actual_start_time,
'actual_end_time': contract.actual_end_time,
'is_contract': contract.is_contract,
'total_area': contract.total_area or 0.0,
})
# ========== 五、承租方/合同编号过滤 ==========
# 前端传入 lessee_id 或 contract_id 时,需要根据合同信息过滤
if lessee_id or contract_id:
allowed_property_ids = set()
for pid, contracts in contract_map.items():
for c in contracts:
# 承租方匹配
if lessee_id and c['lessee_id'] != int(lessee_id):
continue
# 合同编号匹配
if contract_id and c['contract_id'] != int(contract_id):
continue
allowed_property_ids.add(pid)
break
# 过滤 manage_lines,只保留符合条件的物业
manage_lines = manage_lines.filtered(
lambda l: l.yuthon_property_id.id in allowed_property_ids
)
# ========== 六、构建报表行 ==========
rows = []
total_rent_area = 0.0
seen_property_ids = set() # 已计入合计的物业id集合
for idx, line in enumerate(manage_lines, start=1):
property_rec = line.yuthon_property_id
if not property_rec:
continue
# --- 6.1 取合同信息(查询日期范围内生效的合同) ---
contract_code = ""
contract_record_id = False
lessee_name = ""
lessee_record_id = False
contracts_list = contract_map.get(property_rec.id, [])
if contracts_list:
# 优先取查询日期范围内生效的合同
active_contracts = contracts_list
if query_date:
active_contracts = [
c for c in contracts_list
if c['actual_start_time'] and c['actual_start_time'] <= query_date
and (not c['actual_end_time'] or c['actual_end_time'] >= query_date)
]
# 如果没有过滤出有效合同,回退到所有合同
if not active_contracts:
active_contracts = contracts_list
# 收集所有合同的编号和承租方(去重,保持顺序)
code_list = []
lessee_list = []
id_list = []
lessee_id_list = []
for c in active_contracts:
if c['contract_code'] and c['contract_code'] not in code_list:
code_list.append(c['contract_code'])
if c['lessee_name'] and c['lessee_name'] not in lessee_list:
lessee_list.append(c['lessee_name'])
id_list.append(c['contract_id'])
if c['lessee_id']:
lessee_id_list.append(c['lessee_id'])
contract_code = "、".join(code_list)
lessee_name = "、".join(lessee_list)
contract_record_id = id_list[0] if id_list else False
lessee_record_id = lessee_id_list[0] if lessee_id_list else False
# --- 6.2 关键字过滤(物业名称、承租方、合同编号) ---
if keyword:
match = False
kw = keyword.lower()
# 物业名称
if property_rec.name and kw in property_rec.name.lower():
match = True
# 承租方
if lessee_name and kw in lessee_name.lower():
match = True
# 合同编号
if contract_code and kw in contract_code.lower():
match = True
if not match:
continue
# --- 6.3 取产权信息(产权证号、产权地址) ---
title_certificate = ""
property_address = ""
if property_rec.title_information_ids:
valid_titles = property_rec.title_information_ids
if query_date:
valid_titles = valid_titles.filtered(
lambda t: t.start_time and t.start_time <= query_date and
(not t.end_date or t.end_date >= query_date)
)
if valid_titles:
# 按生效日期降序取最新的
latest_title = valid_titles.sorted('start_time', reverse=True)[0]
title_certificate = latest_title.title_certificate or ""
if latest_title.property_message_id:
property_address = latest_title.property_message_id.name or ""
# --- 6.4 片区管理员 ---
admin_display = ""
if line.precinct_id:
admin_display = line.precinct_id.display_name or ""
# --- 6.5 累计统计(按物业id去重,同一物业id只参与一次合计) ---
rent_area = line.rent_area or 0.0
if property_rec.id not in seen_property_ids:
total_rent_area += rent_area
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,
})
# ========== 七、构建统计汇总数据 ==========
stats = [
{'name': '记录总数', 'amount': len(rows)},
{'name': '可租面积合计', 'amount': round(total_rent_area, 4), 'display_amount': f"{round(total_rent_area, 4):.4f}"},
]
_logger.info("[RENT_AREA] query_date=%s, lines=%d, rows=%d, total_area=%.4f",
query_date, len(manage_lines), len(rows), total_rent_area)
return {"rows": rows, "stats": stats}