报表
This commit is contained in:
@@ -30,6 +30,7 @@ class LeasedAreaReport(models.Model):
|
||||
6. 片区管理员从第一个物业的 manage.information.line 取(按查询日期匹配)
|
||||
7. 支持按物业名称(property_id)、承租方(lessee_id)、合同编号(contract_id)搜索
|
||||
8. 按物业筛选时,只保留包含该物业的合同,但合同仍显示全部物业信息(物业名称、面积均累加全部物业)
|
||||
9. 同一物业出现在多个合同时,面积只计算一次(归属到 start_time 最晚的合同)
|
||||
"""
|
||||
|
||||
# ========== 一、参数解析 ==========
|
||||
@@ -115,37 +116,27 @@ class LeasedAreaReport(models.Model):
|
||||
if ml.yuthon_property_id:
|
||||
manage_map[ml.yuthon_property_id.id] = ml
|
||||
|
||||
# ========== 六、构建报表行(按合同分组,一个合同一行) ==========
|
||||
rows = []
|
||||
total_property_area = 0.0 # 物业已租面积合计(按物业id去重)
|
||||
total_contract_area = 0.0 # 合同已租面积合计(按物业id去重)
|
||||
seen_property_ids = set() # 已计入合计的物业id集合
|
||||
# ========== 六、第一轮遍历:过滤 + 收集有效合同 ==========
|
||||
# 先把所有过滤条件跑一遍,收集最终出现在报表中的合同列表
|
||||
# 再基于过滤后的合同做面积归属预扫描,避免被过滤掉的合同抢走归属权
|
||||
filtered_contracts = [] # [(contract, lines, first_cl, first_property, manage_line)]
|
||||
|
||||
for idx, contract in enumerate(contracts, start=1):
|
||||
for contract in contracts:
|
||||
lines = lines_by_contract.get(contract.id, [])
|
||||
if not lines:
|
||||
continue
|
||||
|
||||
# --- 时间过滤:基于 actual_start_time 和 actual_end_time ---
|
||||
# 规则:
|
||||
# 1. actual_start_time 为空 → 查不到(数据不完整)
|
||||
# 2. actual_start_time > 查询月末 → 合同在查询月之后才启动,查不到
|
||||
# 3. 1号起租:actual_end_time >= 查询月初 → 能查到
|
||||
# 4. 非1号起租:actual_end_time 往前推一个月 >= 查询月初 → 能查到
|
||||
# 5. actual_end_time 为空 → 查不到(数据不完整)
|
||||
if query_date:
|
||||
# 实际开始时间为空,查不到
|
||||
if not contract.actual_start_time:
|
||||
continue
|
||||
|
||||
# 条件1:实际开始时间在查询月之后就查不到
|
||||
if contract.actual_start_time > query_month_last:
|
||||
continue
|
||||
|
||||
# 实际结束时间为空,查不到
|
||||
if not contract.actual_end_time:
|
||||
continue
|
||||
|
||||
# 条件2:结束时间判断
|
||||
if contract.actual_start_time.day == 1:
|
||||
# 1号起租:actual_end_time >= 查询月初就能查到
|
||||
@@ -156,7 +147,6 @@ class LeasedAreaReport(models.Model):
|
||||
end_year = contract.actual_end_time.year
|
||||
end_month = contract.actual_end_time.month
|
||||
end_day = contract.actual_end_time.day
|
||||
|
||||
# 前一个月
|
||||
if end_month == 1:
|
||||
pushed_year = end_year - 1
|
||||
@@ -164,22 +154,18 @@ class LeasedAreaReport(models.Model):
|
||||
else:
|
||||
pushed_year = end_year
|
||||
pushed_month = end_month - 1
|
||||
|
||||
# 处理日期(如前一个月没有这么多天,取最后一天)
|
||||
_, pushed_last_day = calendar.monthrange(pushed_year, pushed_month)
|
||||
pushed_day = min(end_day, pushed_last_day)
|
||||
pushed_end = datetime.date(pushed_year, pushed_month, pushed_day)
|
||||
|
||||
if pushed_end < query_month_first:
|
||||
continue
|
||||
|
||||
# --- 6.1 合同信息 ---
|
||||
# --- 合同信息(提前取,后面关键字过滤需要) ---
|
||||
contract_code = contract.contract_code or ""
|
||||
contract_record_id = contract.id
|
||||
lessee_name = contract.lessee_id.name if contract.lessee_id else ""
|
||||
lessee_record_id = contract.lessee_id.id if contract.lessee_id else False
|
||||
|
||||
# --- 6.2 关键字过滤(物业名称、承租方、合同编号) ---
|
||||
# --- 关键字过滤(物业名称、承租方、合同编号) ---
|
||||
if keyword:
|
||||
match = False
|
||||
kw = keyword.lower()
|
||||
@@ -199,13 +185,8 @@ class LeasedAreaReport(models.Model):
|
||||
if not match:
|
||||
continue
|
||||
|
||||
# --- 6.3 权属类型过滤 ---
|
||||
# 取第一个物业的管理信息做判断
|
||||
first_cl = lines[0]
|
||||
first_property = first_cl.address_id
|
||||
manage_line = manage_map.get(first_property.id) if first_property else None
|
||||
# --- 权属类型过滤 ---
|
||||
if condition:
|
||||
# 遍历该合同下所有物业,只要有一个匹配就保留
|
||||
matched = False
|
||||
for cl in lines:
|
||||
ml = manage_map.get(cl.address_id.id) if cl.address_id else None
|
||||
@@ -215,7 +196,7 @@ class LeasedAreaReport(models.Model):
|
||||
if not matched:
|
||||
continue
|
||||
|
||||
# --- 6.4 所属片区/公司过滤(通过管理信息) ---
|
||||
# --- 所属片区/公司过滤(通过管理信息) ---
|
||||
if partner_id:
|
||||
matched = False
|
||||
for cl in lines:
|
||||
@@ -235,32 +216,109 @@ class LeasedAreaReport(models.Model):
|
||||
if not matched:
|
||||
continue
|
||||
|
||||
# --- 6.5 片区管理员(取第一个物业的片区管理员) ---
|
||||
# 通过所有过滤,加入有效合同列表
|
||||
first_cl = lines[0]
|
||||
first_property = first_cl.address_id
|
||||
manage_line = manage_map.get(first_property.id) if first_property else None
|
||||
filtered_contracts.append((contract, lines, first_cl, first_property, manage_line))
|
||||
|
||||
# ========== 七、预扫描:确定每个物业id的面积计算归属 ==========
|
||||
# 仅基于过滤后的有效合同做归属判断,避免被过滤掉的合同抢走归属权
|
||||
# 规则:同一物业出现在多个有效合同时,归属到 actual_start_time 更晚的合同
|
||||
pid_owner_contract = {} # pid → (contract_id, actual_start_time)
|
||||
|
||||
for contract, lines, _, _, _ in filtered_contracts:
|
||||
if not contract.actual_start_time:
|
||||
continue
|
||||
for cl in lines:
|
||||
if not cl.address_id:
|
||||
continue
|
||||
pid = cl.address_id.id
|
||||
existing = pid_owner_contract.get(pid)
|
||||
if existing is None:
|
||||
# 首次见到该物业,记录当前合同为候选
|
||||
pid_owner_contract[pid] = (contract.id, contract.actual_start_time)
|
||||
elif contract.actual_start_time > existing[1]:
|
||||
# 当前合同的 start_time 更晚,更新归属到当前合同
|
||||
pid_owner_contract[pid] = (contract.id, contract.actual_start_time)
|
||||
|
||||
# ========== 八、第二轮遍历:构建报表行 + 计算合计 ==========
|
||||
rows = []
|
||||
total_property_area = 0.0 # 物业已租面积合计
|
||||
total_contract_area = 0.0 # 合同已租面积合计
|
||||
counted_pids = set() # 已参与合计计算的物业id
|
||||
|
||||
for idx, (contract, lines, first_cl, first_property, manage_line) in enumerate(filtered_contracts, start=1):
|
||||
|
||||
# --- 8.1 合同信息 ---
|
||||
contract_code = contract.contract_code or ""
|
||||
contract_record_id = contract.id
|
||||
lessee_name = contract.lessee_id.name if contract.lessee_id else ""
|
||||
lessee_record_id = contract.lessee_id.id if contract.lessee_id else False
|
||||
|
||||
# --- 8.2 片区管理员(取第一个物业的片区管理员) ---
|
||||
admin_display = ""
|
||||
if manage_line and manage_line.precinct_id:
|
||||
admin_display = manage_line.precinct_id.display_name or ""
|
||||
|
||||
# --- 6.6 物业已租面积:该合同下所有物业 property_area 之和 ---
|
||||
# 即 contract.total_area,但逐个计算更可靠
|
||||
# --- 8.3 物业已租面积:该合同下所有物业 property_area 之和 ---
|
||||
property_area_sum = sum((cl.property_area or 0.0) for cl in lines)
|
||||
# 合同已租面积
|
||||
contract_area = contract.total_area or 0.0
|
||||
|
||||
# 如果 property_area_sum 和 contract.total_area 差异较大,用 property_area_sum
|
||||
# 否则以 contract.total_area 为准
|
||||
property_area = property_area_sum if property_area_sum > 0 else contract_area
|
||||
|
||||
# --- 合计数去重:按物业id去重,同一物业id只参与一次合计 ---
|
||||
# 解析该合同下所有物业id
|
||||
# --- 8.4 合计数去重:同一物业id只计算一次,归属到 start_time 最晚的合同 ---
|
||||
# 规则:物业id只出现一次→正常计算;出现多次→只在start_time最晚的合同中计算
|
||||
# 解析该合同下所有物业id及对应面积
|
||||
property_ids_in_row = [cl.address_id.id for cl in lines if cl.address_id]
|
||||
has_new = any(pid not in seen_property_ids for pid in property_ids_in_row)
|
||||
if has_new:
|
||||
total_property_area += property_area
|
||||
total_contract_area += contract_area
|
||||
for pid in property_ids_in_row:
|
||||
seen_property_ids.add(pid)
|
||||
pid_area_map = {cl.address_id.id: cl.property_area or 0.0 for cl in lines if cl.address_id}
|
||||
# 找出归属于当前合同的物业(pid_owner_contract 中记录的 owner == 当前合同)
|
||||
owner_pids = [
|
||||
pid for pid in property_ids_in_row
|
||||
if pid_owner_contract.get(pid, (None, None))[0] == contract.id
|
||||
]
|
||||
# 其中尚未参与过计算的
|
||||
new_owner_pids = [pid for pid in owner_pids if pid not in counted_pids]
|
||||
# 不归属当前合同的物业(属于start_time更晚的其他合同,不参与计算但仍展示)
|
||||
skip_pids = [pid for pid in property_ids_in_row if pid not in owner_pids]
|
||||
|
||||
# --- 6.7 物业名称和物业ID:列出该合同下所有物业 ---
|
||||
# 调试日志
|
||||
_logger.info(
|
||||
"[LEASED_AREA_DEBUG] 合同=%s(id=%s), start=%s, 所有物业id=%s, "
|
||||
"归属本合同id=%s, 新计算id=%s, 跳过id(归属更晚合同)=%s, "
|
||||
"property_area=%.4f, contract_area=%.4f",
|
||||
contract_code, contract.id, contract.actual_start_time,
|
||||
property_ids_in_row, owner_pids, new_owner_pids, skip_pids,
|
||||
property_area, contract_area,
|
||||
)
|
||||
|
||||
if new_owner_pids and property_area > 0:
|
||||
# 直接按归属物业的 property_area 逐个累加
|
||||
add_prop_area = sum(pid_area_map.get(pid, 0.0) for pid in new_owner_pids)
|
||||
total_property_area += add_prop_area
|
||||
# 合同已租面积按归属物业面积占该行总物业面积的比例分配
|
||||
if property_area_sum > 0:
|
||||
ratio = add_prop_area / property_area_sum
|
||||
else:
|
||||
ratio = 1.0
|
||||
total_contract_area += contract_area * ratio
|
||||
|
||||
_logger.info(
|
||||
"[LEASED_AREA_DEBUG] → 计入合计: add_prop_area=%.4f, ratio=%.4f, "
|
||||
"累计 property=%.4f, contract=%.4f",
|
||||
add_prop_area, ratio,
|
||||
total_property_area, total_contract_area,
|
||||
)
|
||||
else:
|
||||
_logger.info("[LEASED_AREA_DEBUG] → 未计入合计(无新归属物业)")
|
||||
|
||||
# 只标记实际参与计算的物业为已处理
|
||||
for pid in new_owner_pids:
|
||||
counted_pids.add(pid)
|
||||
|
||||
# --- 8.5 物业名称和物业ID:列出该合同下所有物业 ---
|
||||
property_names = []
|
||||
property_ids_strs = []
|
||||
for cl in lines:
|
||||
@@ -280,7 +338,7 @@ class LeasedAreaReport(models.Model):
|
||||
else:
|
||||
property_name_value = display_property_name
|
||||
|
||||
# --- 6.8 构建行数据(物业名称、合同编号、承租方支持穿透) ---
|
||||
# --- 8.6 构建行数据(物业名称、合同编号、承租方支持穿透) ---
|
||||
# 实际起止时间格式化
|
||||
actual_start = contract.actual_start_time
|
||||
if actual_start:
|
||||
@@ -314,7 +372,7 @@ class LeasedAreaReport(models.Model):
|
||||
"片区管理员": admin_display,
|
||||
})
|
||||
|
||||
# ========== 七、构建统计汇总数据 ==========
|
||||
# ========== 九、构建统计汇总数据 ==========
|
||||
stats = [
|
||||
{'name': '记录总数', 'amount': len(rows)},
|
||||
{'name': '物业已租面积合计', 'amount': round(total_property_area, 4), 'display_amount': f"{round(total_property_area, 4):.4f}"},
|
||||
|
||||
Reference in New Issue
Block a user