Files
gzth/yuthon_report/models/deposit_pre_received_report.py
T
2026-06-29 16:22:31 +08:00

187 lines
7.8 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
import calendar
from odoo import api, models
from .report_utils import _id_list # partner_id 多选适配工具
class DepositPreReceivedReport(models.Model):
_inherit = 'property.accounts.receivable'
@api.model
def get_deposit_pre_received_report(self, params):
"""
押金前期实收报表:
- 数据源:property.accounts.receivable
- 管理费用类型为"押金"
- 非作废记录
- 金额:original_amount - discount_amount
- 状态取数逻辑:
- 已收(paid):payout_date 存在且 < 查询月初(前期实收 = 查询月之前收到,不含查询月)
- 已退(yes):退款日期(has_date) > 查询月末 → 查询时还没退,应包含
退款日期(has_date) <= 查询月末 → 已退,不包含
(已退状态不受 payout_date 约束)
"""
date_start = params.get("date_start")
summary_only = params.get("summary_only", False)
query_date = datetime.datetime.fromisoformat(date_start).date() if date_start else datetime.date.today()
month_first = query_date.replace(day=1)
_, last_day = calendar.monthrange(query_date.year, query_date.month)
month_last = query_date.replace(day=last_day)
# 放宽 domain,只做基础筛选
domain = [
('management_type_id.name', '=', '押金'),
('is_invalid', '!=', True),
('deposit_state', 'in', ['paid', 'yes', 'no']),
]
keyword = params.get("keyword")
if keyword:
domain += ['|', '|',
('contract_id.contract_code', 'ilike', keyword),
('property_name', 'ilike', keyword),
('lessee_id.name', 'ilike', keyword),
]
condition = params.get('condition')
partner_id = _id_list(params.get('partner_id')) # 支持多选,转为列表
company_id = _id_list(params.get('company_id'))
property_id = params.get('property_id')
lessee_id = params.get('lessee_id')
contract_id = params.get('contract_id')
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))
if property_id:
domain.append(('property_ids', 'in', [property_id]))
if lessee_id:
domain.append(('lessee_id', '=', lessee_id))
if contract_id:
domain.append(('contract_id', '=', contract_id))
lines = self.sudo().search(domain, order="contract_id asc")
# 内存过滤:按状态分别处理
# - 已收(paid):收款日期必须存在且 < 查询月初(前期实收 = 查询月之前收到)
# - 待退(no):收款日期存在且 < 查询月初(已收但未退,属于前期实收)
# - 已退(yes):退款日期 > 查询月末 且 收款日期 < 查询月初(查询时还没退,且是前期收的)
lines = lines.filtered(
lambda l: (
l.deposit_state == 'paid' and l.payout_date and l.payout_date < month_first
) or (
l.deposit_state == 'no' and l.payout_date and l.payout_date < month_first
) or (
l.deposit_state == 'yes' and l.has_date and l.has_date > month_last
and l.payout_date and l.payout_date < month_first
)
)
if summary_only:
total_amount = sum(round((rec.original_amount or 0.0) - (rec.discount_amount or 0.0), 2) for rec in lines)
stats = [
{'name': '记录总数', 'amount': len(lines)},
{'name': '合计金额', 'amount': round(total_amount, 2)},
]
return {"rows": [], "stats": stats}
rows = []
total_amount = 0.0
for idx, rec in enumerate(lines, start=1):
amount = round((rec.original_amount or 0.0) - (rec.discount_amount or 0.0), 2)
total_amount += amount
contract = rec.contract_id
# 物业穿透
property_val = ''
if contract and contract.lease_ids and contract.lease_ids[0].address_id:
prop = contract.lease_ids[0].address_id
property_val = {
'model': 'yuthon.property',
'id': prop.id,
'display_name': rec.property_name or prop.name,
}
elif rec:
property_val = rec.property_name or ''
# 状态映射:deposit_state 值 → 中文显示名
state_display = {
'unpaid': '未收',
'no_paid': '收部分',
'paid': '已收',
'no': '待退',
'yes': '已退',
}
rows.append({
"序号": idx,
"状态": state_display.get(rec.deposit_state, rec.deposit_state or ""),
"合同编号": {
'model': 'property.lease.contract',
'id': contract.id,
'display_name': contract.contract_code,
} if contract else "",
"物业名称": property_val,
"承租方名称": {
'model': 'property.tenant.information',
'id': rec.lessee_id.id,
'display_name': rec.lessee_id.name,
} if rec.lessee_id else "",
"收款日期": rec.payout_date.strftime("%Y-%m-%d") if rec.payout_date else "",
"退款日期": rec.has_date.strftime("%Y-%m-%d") if rec.has_date else "", # 退款日期字段
"金额": amount,
"应收金额": round((rec.original_amount or 0.0) - (rec.discount_amount or 0.0), 2),
"所属公司": rec.company_id.name if rec.company_id else "",
"管理员": {
'model': 'res.users',
'id': rec.admin_id.id,
'display_name': rec.admin_id.name,
} if rec.admin_id else "",
})
stats = [
{'name': '记录总数', 'amount': len(rows)},
{'name': '合计金额', 'amount': round(total_amount, 2)},
]
return {"rows": rows, "stats": stats}
@api.model
def get_receivable_filter_domains(self, params):
"""押金前期实收报表:返回物业/承租方/合同的候选 ID 集合。
注意:不应用 due_date 日期过滤,因为押金报表的日期逻辑基于 payout_date/has_date,
与 receivable_flow_report 的 due_date 过滤不一致,会导致下拉候选项缺失。
"""
domain = [
('management_type_id.name', '=', '押金'),
('is_invalid', '!=', True),
]
company_id = _id_list(params.get('company_id'))
property_id = params.get('property_id')
lessee_id = params.get('lessee_id')
contract_id = params.get('contract_id')
if company_id:
domain.append(('company_id', 'in', company_id))
if property_id:
domain.append(('property_ids', 'in', [property_id]))
if lessee_id:
domain.append(('lessee_id', '=', lessee_id))
if contract_id:
domain.append(('contract_id', '=', contract_id))
receivables = self.sudo().search(domain)
property_ids = set(receivables.mapped('property_ids').ids)
lessee_ids = set(receivables.mapped('lessee_id').ids)
contract_ids = set(receivables.mapped('contract_id').ids)
return {
'property_ids': list(property_ids),
'lessee_ids': list(lessee_ids),
'contract_ids': list(contract_ids),
}