261 lines
12 KiB
Python
261 lines
12 KiB
Python
import datetime
|
||
import logging
|
||
|
||
from odoo import api, models
|
||
from .report_utils import _id_list # partner_id 多选适配工具
|
||
|
||
_logger = logging.getLogger(__name__)
|
||
|
||
|
||
class ManagementAreaReport(models.Model):
|
||
_name = 'management.area.report'
|
||
_description = '管理面积报表'
|
||
_auto = False
|
||
|
||
@api.model
|
||
def get_management_area_report(self, params):
|
||
"""管理面积报表数据源。
|
||
|
||
核心逻辑:
|
||
1. 数据源为 manage.information.line(管理信息明细行)
|
||
2. 日期过滤:生效日期 <= 查询日期 <= 失效日期(失效日期为空表示至今有效)
|
||
3. 管理面积直接取 manage.information.line.management_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 = _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')
|
||
contract_id = params.get('contract_id')
|
||
summary_only = params.get("summary_only", False)
|
||
|
||
# ========== 二、查询日期解析 ==========
|
||
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("[MANAGEMENT_AREA] 日期解析失败: %s", query_date_str)
|
||
pass
|
||
|
||
# 没有查询日期时不做时间过滤(返回所有记录)
|
||
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', 'in', partner_id)) # 多选用 in
|
||
if company_id:
|
||
domain.append(('company_id', 'in', 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_code, lessee_name, contract_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,
|
||
})
|
||
|
||
# ========== 五、承租方/合同编号过滤 ==========
|
||
# 前端传入 lessee_id 或 contract_id 时,需要根据合同信息过滤
|
||
# 找出符合条件的物业 ID 集合,用于过滤 manage_lines
|
||
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 = []
|
||
row_count = 0
|
||
total_management_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 片区管理员 ---
|
||
# precinct_id.display_name 格式为 "片区名称管理员姓名",如 "二D梁嘉辉"
|
||
admin_display = ""
|
||
if line.precinct_id:
|
||
admin_display = line.precinct_id.display_name or ""
|
||
|
||
# --- 6.5 累计统计(按物业id去重,同一物业id只参与一次合计) ---
|
||
management_area = line.management_area or 0.0
|
||
if property_rec.id not in seen_property_ids:
|
||
total_management_area += management_area
|
||
seen_property_ids.add(property_rec.id)
|
||
|
||
# --- 6.6 构建行数据(物业名称、合同编号、承租方支持穿透) ---
|
||
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': row_count},
|
||
{'name': '管理面积合计', 'amount': round(total_management_area, 4), 'display_amount': f"{round(total_management_area, 4):.4f}"},
|
||
]
|
||
|
||
_logger.info("[MANAGEMENT_AREA] query_date=%s, lines=%d, rows=%d, total_area=%.4f",
|
||
query_date, len(manage_lines), len(rows), total_management_area)
|
||
|
||
return {"rows": rows, "stats": stats}
|