252 lines
11 KiB
Python
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}
|