累计欠收报表

This commit is contained in:
李鹏宇
2026-02-26 15:10:54 +08:00
parent e0bd6e5dd8
commit fb17bb66fd
4 changed files with 81 additions and 19 deletions
@@ -11,7 +11,9 @@ class CumulativeArrearsReport(models.Model):
"""
累计欠收报表:
- 依据应收日期过滤(欠租所属月份)
- 金额:月租金取合同租金(contract_id.rent),欠租金额取 submitted,面积取合同 total_area
- 显示:应收金额、已收金额(截止查询日期)、当前欠收、计费区间(应收开始-应收结束)
- 金额:应收金额取original_amount,已收金额通过对账记录表计算(截止查询日期的累计收款),当前欠收=应收-已收
- 关键:通过receivable_modify_line表的reconciliation_date来计算截止特定日期的已收金额
"""
domain = []
@@ -30,6 +32,10 @@ class CumulativeArrearsReport(models.Model):
if date_end:
end_dt = datetime.datetime.fromisoformat(date_end).date()
domain.append(("due_date", "<=", end_dt))
# 将查询截止日期保存,用于后续计算已收金额
query_end_date = end_dt
else:
query_end_date = datetime.date.today()
condition = params.get('condition')
partner_id = params.get('partner_id')
@@ -47,19 +53,63 @@ class CumulativeArrearsReport(models.Model):
receivables = self.search(domain, order="due_date asc")
# 预先查询所有应收记录的对账记录(截止查询日期)
# 使用字典存储每个应收记录的已收金额,避免在循环中多次查询
receivable_ids = receivables.ids
modify_lines = self.env['receivable.modify.line'].search([
('receivable_id', 'in', receivable_ids),
('reconciliation_date', '<=', query_end_date)
])
# 构建应收ID -> 已收金额的映射
received_amount_map = {}
for line in modify_lines:
rec_id = line.receivable_id.id
if rec_id not in received_amount_map:
received_amount_map[rec_id] = 0.0
received_amount_map[rec_id] += line.amount or 0.0
rows = []
type_totals = {}
total_arrears = 0.0
total_original = 0.0 # 总应收
total_received = 0.0 # 总已收
total_arrears = 0.0 # 总欠收
for idx, rec in enumerate(receivables, start=1):
contract = rec.contract_id
rent = contract.rent or rec.original_amount or 0.0
arrears = rec.submitted or 0.0
# 应收金额
original = rec.original_amount or 0.0
# 已收金额:从预先构建的映射中获取(截止查询日期的累计收款)
received = received_amount_map.get(rec.id, 0.0)
# 当前欠收 = 应收 - 已收 - 减免
discount = rec.discount_amount or 0.0
arrears = original - received - discount
# 确保欠收不为负数
if arrears < 0:
arrears = 0.0
# 累计统计
total_original += original
total_received += received
total_arrears += arrears
# 按类型统计
type_name = rec.management_type_id.name if rec.management_type_id else "未分类"
if type_name not in type_totals:
type_totals[type_name] = {'arrears': 0.0}
type_totals[type_name] = {'original': 0.0, 'received': 0.0, 'arrears': 0.0}
type_totals[type_name]['original'] += original
type_totals[type_name]['received'] += received
type_totals[type_name]['arrears'] += arrears
# 计费区间(应收开始 ~ 应收结束)
billing_period = ""
if rec.due_date and rec.due_date_end:
billing_period = f"{rec.due_date.strftime('%Y-%m-%d')} ~ {rec.due_date_end.strftime('%Y-%m-%d')}"
elif rec.due_date:
billing_period = rec.due_date.strftime("%Y-%m-%d")
rows.append({
"序号": idx,
@@ -67,17 +117,29 @@ class CumulativeArrearsReport(models.Model):
"面积": contract.total_area if contract and contract.total_area else "",
"承租方名称": rec.lessee_id.name if rec.lessee_id else "",
"合同编号": contract.contract_code if contract else "",
"月租金": round(rent, 2),
"欠租金额": round(arrears, 2),
"欠租所属月份": rec.due_date.strftime("%Y-%m-%d") if rec.due_date else "",
"应收类型": type_name,
"应收金额": round(original, 2),
"已收金额": round(received, 2),
"当前欠收": round(arrears, 2),
"计费区间": billing_period,
"所属公司": contract.lessor_id.referred if contract and contract.lessor_id and hasattr(contract.lessor_id, 'referred') else contract.lessor_id.name if contract and contract.lessor_id else "",
"管理员": rec.admin_id.name if rec.admin_id else "",
})
summary_stats = []
for type_name in sorted(type_totals.keys()):
summary_stats.append({'name': type_name, 'amount': round(type_totals[type_name]['arrears'], 2)})
summary_stats.append({'name': '总合计', 'amount': round(total_arrears, 2)})
summary_stats.append({
'name': type_name,
'original': round(type_totals[type_name]['original'], 2),
'received': round(type_totals[type_name]['received'], 2),
'arrears': round(type_totals[type_name]['arrears'], 2)
})
summary_stats.append({
'name': '总合计',
'original': round(total_original, 2),
'received': round(total_received, 2),
'arrears': round(total_arrears, 2)
})
return {"rows": rows, "stats": summary_stats}
@@ -12,10 +12,8 @@ class StaffPositionReport(models.Model):
返回当前有效岗位的编制、在岗、缺编等信息。
"""
domain = [
("is_enable", "=", True),
("establishment_quotas", ">", 0),
]
# 不再过滤is_enable和establishment_quotas,查询所有岗位
domain = []
company_id = params.get("company_id")
department_id = params.get("department_id")
@@ -16,9 +16,11 @@
{"headerName": "面积(m²)", "field": "面积", "flex": 1},
{"headerName": "承租方名称", "field": "承租方名称", "flex": 1.4},
{"headerName": "合同编号", "field": "合同编号", "flex": 1.2},
{"headerName": "月租金", "field": "月租金", "flex": 1, "type": "number"},
{"headerName": "欠租金额", "field": "欠租金额", "flex": 1, "type": "number"},
{"headerName": "欠租所属月份", "field": "欠租所属月份", "flex": 1.2},
{"headerName": "应收类型", "field": "应收类型", "flex": 1},
{"headerName": "应收金额", "field": "应收金额", "flex": 1, "type": "number"},
{"headerName": "已收金额", "field": "已收金额", "flex": 1, "type": "number"},
{"headerName": "当前欠收", "field": "当前欠收", "flex": 1, "type": "number"},
{"headerName": "计费区间", "field": "计费区间", "flex": 1.5},
{"headerName": "所属公司", "field": "所属公司", "flex": 1},
{"headerName": "管理员", "field": "管理员", "flex": 1}
]
@@ -2,7 +2,7 @@
<odoo>
<data>
<record id="action_overdue_amount_report" model="ir.actions.client">
<field name="name">累计欠收</field>
<field name="name">欠费明细</field>
<field name="tag">receivable_report_client</field>
<field name="context">
{
@@ -29,7 +29,7 @@
<menuitem
id="menu_overdue_amount_report"
name="累计欠收"
name="欠费明细"
parent="yuthon_property_approval.property_approval_report_menu"
action="action_overdue_amount_report"
sequence="10"