Files
gzth/yuthon_report/models/current_month_received_report.py
T
2026-06-22 19:25:07 +08:00

150 lines
6.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import datetime
from odoo import api, models
from .report_utils import _id_list # 导入工具函数:将参数转换为列表格式,支持多选筛选
class CurrentMonthReceivedReport(models.Model):
_inherit = 'receivable.modify.line'
@api.model
def get_current_month_received_report(self, params):
"""归属本月实收报表:按收款行自身的 reconciliation_date(对账日期)筛选已收金额。"""
summary_only = params.get("summary_only", False)
domain = []
keyword = params.get("keyword")
if keyword:
domain += [
'|', '|',
('receivable_id.contract_id.contract_code', 'ilike', keyword),
('receivable_id.property_name', 'ilike', keyword),
('receivable_id.lessee_id.name', 'ilike', keyword),
]
date_start = params.get("date_start")
date_end = params.get("date_end")
start_dt = datetime.datetime.fromisoformat(date_start).date() if date_start else None
end_dt = datetime.datetime.fromisoformat(date_end).date() if date_end else None
if start_dt and end_dt:
# 用 reconciliation_date(收款行自身的对账日期)做月份筛选
# 这样每笔收款行按自己实际收款日期归属到对应月份,不会因账单级 payout_date 被覆盖而误归月份
import calendar as _cal
# query_month_end 取查询日所在月的月末,确保整月覆盖
query_month_start = start_dt.replace(day=1)
last_day = _cal.monthrange(start_dt.year, start_dt.month)[1]
query_month_end = start_dt.replace(day=last_day)
domain += [
('reconciliation_date', '>=', query_month_start),
('reconciliation_date', '<=', query_month_end),
]
condition = params.get('condition')
partner_id = _id_list(params.get('partner_id')) # 将partner_id转换为列表,用于多选筛选
company_id = _id_list(params.get('company_id'))
management_type_id = params.get('management_type_id')
property_id = params.get('property_id')
lessee_id = params.get('lessee_id')
contract_id = params.get('contract_id')
if condition:
domain.append(('receivable_id.tenure_type', '=', condition))
if partner_id:
domain.append(('receivable_id.precinct_id', 'in', partner_id)) # 使用in操作符支持多选片区筛选
if company_id:
domain.append(('receivable_id.company_id', 'in', company_id))
if property_id:
domain.append(('receivable_id.property_ids', 'in', [property_id]))
if lessee_id:
domain.append(('receivable_id.lessee_id', '=', lessee_id))
if contract_id:
domain.append(('receivable_id.contract_id', '=', contract_id))
# 经营收入只统计租金类,排除押金、违约金
exclude_types = ['押金', '违约金']
if management_type_id:
domain.append(('receivable_id.management_type_id', '=', management_type_id))
else:
domain.append(('receivable_id.management_type_id.name', 'not in', exclude_types))
lines = self.search(domain, order="receivable_id asc")
# --- 排除跨月预收 ---
# 归属本月 = 实收总额 - 本月预收
# 本月预收定义:reconciliation_date < due_date 且跨月(不在同年同月)
# 这类收款行只计入「本月预收」报表,不应再计入「归属本月」,否则会被重复统计
filtered_lines = []
for line in lines:
rec_inner = line.receivable_id
if not rec_inner:
filtered_lines.append(line)
continue
# 取对账日期(统一转 date)
pay_date = line.reconciliation_date
if not pay_date:
filtered_lines.append(line)
continue
if hasattr(pay_date, 'date'):
pay_date = pay_date.date()
# 取应收日期
due = rec_inner.due_date
if not due:
filtered_lines.append(line)
continue
# 跨月预收:reconciliation_date < due_date 且不在同年同月 → 排除
if pay_date < due and not (pay_date.year == due.year and pay_date.month == due.month):
continue # 这笔归到本月预收,不计入归属本月
filtered_lines.append(line)
if summary_only:
total_amount = sum(line.amount or 0.0 for line in filtered_lines)
stats = [{"name": "实收总额", "amount": round(total_amount, 2)}]
return {"rows": [], "stats": stats}
rows = []
total_amount = 0.0
for idx, line in enumerate(filtered_lines, start=1):
rec = line.receivable_id
amount = line.amount or 0.0 # 已对金额
total_amount += amount
# 应收金额 = original_amount - discount_amount
receivable_amount = round((rec.original_amount or 0.0) - (rec.discount_amount or 0.0), 2)
rows.append({
"序号": idx,
"合同编号": {
'model': 'property.lease.contract',
'id': rec.contract_id.id,
'display_name': rec.contract_id.contract_code,
} if rec and rec.contract_id else "",
"物业名称": {
'model': 'yuthon.property',
'id': rec.contract_id.lease_ids[0].address_id.id,
'display_name': rec.property_name,
} if rec and rec.contract_id and rec.contract_id.lease_ids and rec.contract_id.lease_ids[0].address_id else rec.property_name or "",
"承租方名称": {
'model': 'property.tenant.information',
'id': rec.lessee_id.id,
'display_name': rec.lessee_id.name,
} if rec and rec.lessee_id else "",
"应收开始": rec.due_date.strftime('%Y-%m-%d') if rec and rec.due_date else '',
"应收结束": rec.due_date_end.strftime('%Y-%m-%d') if rec and rec.due_date_end else '',
"金额": round(amount, 2),
"应收金额": receivable_amount,
"所属公司": rec.company_id.referred if rec and rec.company_id and hasattr(rec.company_id, 'referred') else rec.company_id.name if rec and rec.company_id else "",
"管理员": {
'model': 'res.users',
'id': rec.admin_id.id,
'display_name': rec.admin_id.name,
} if rec and rec.admin_id else "",
})
return {
"rows": rows,
"stats": [
{"name": "实收总额", "amount": round(total_amount, 2)},
],
}