This commit is contained in:
李鹏宇
2026-06-23 14:57:36 +08:00
parent b3a2bc6e73
commit 0e3e6072fc
14 changed files with 300 additions and 318 deletions
@@ -11,6 +11,10 @@
<field name="form_fee"/>
<field name="expenses_type"/>
<field name="users_id"/>
<!-- 支付日期:与表单视图一致 -->
<field name="pay_date"/>
<!-- 权属类型:selection字段,与表单视图一致 -->
<field name="tenure_type"/>
<field name="approve_state" widget="badge"
decoration-danger="approve_state == 'end'"
decoration-success="approve_state == 'pending'"/>
@@ -8,18 +8,19 @@ from .report_utils import _id_list # 导入 ID 列表解析工具函数,支
_logger = logging.getLogger(__name__)
class PropertyBillsList(models.Model):
_inherit = 'property.bills.list'
class PropertyFeeForm(models.Model):
# 改为继承 property.fee.form 模型
_inherit = 'property.fee.form'
@api.model
def get_assessment_fee_monthly_report(self, params):
"""评估费本月支出报表。
数据源:property.bills.list
数据源:property.fee.form
过滤条件:
- management_type_id.name = '评估费'
- actual_date(实付日期)在查询月本月范围内(月初~月末)
字段:应付日期、实付日期、应付、实付、标题
- expenses_type.name = '评估费'
- pay_date(支付日期)在查询月本月范围内(月初~月末)
字段:标题、所属公司、支付金额、申请人、支付日期、权属类型
"""
# ========== 一、参数解析 ==========
date_start = params.get("date_start")
@@ -41,68 +42,61 @@ class PropertyBillsList(models.Model):
# ========== 三、构建domain ==========
domain = [
('management_type_id.name', '=', '评估费'),
('actual_date', '>=', month_first),
('actual_date', '<=', month_last),
('expenses_type.name', '=', '评估费'), # 支出类型为评估费
('pay_date', '>=', month_first), # 支付日期在查询月月初及以后
('pay_date', '<=', month_last), # 支付日期在查询月月末及以前
]
# 关键字过滤
# 关键字过滤:按标题模糊搜索
if keyword:
domain += ['|', '|', '|',
('name', 'ilike', keyword),
('contract_id.contract_code', 'ilike', keyword),
('lessee_id.name', 'ilike', keyword),
('property_name', 'ilike', keyword),
]
domain += ['|', ('name', 'ilike', keyword),
('users_id.name', 'ilike', keyword)]
# 公司/片区过滤
# 公司过滤:company_id1 为所属公司字段
company_id = _id_list(params.get('company_id'))
partner_id = _id_list(params.get('partner_id')) # 解析片区 ID 为整数列表(多选支持)
if company_id:
domain.append(('lessor_id', 'in', company_id))
domain.append(('company_id1', 'in', company_id))
# 片区(partner_id):通过 precinct.message -> employee -> user 关联申请人
if partner_id:
domain += ['|',
('contract_id.precinct_id', 'in', partner_id), # 合同片区过滤(多选支持)
('receivables_id.precinct_id', 'in', partner_id), # 应收记录片区过滤(多选支持)
]
precincts = self.env['yuthon.precinct.message'].sudo().browse(partner_id)
user_ids = precincts.mapped('employee_id.user_id').ids
if user_ids:
domain.append(('users_id', 'in', user_ids))
else:
domain.append(('id', '=', False))
# 查询记录
records = self.sudo().search(domain)
records = self.sudo().search(domain, order='pay_date asc')
# ========== 四、构建报表行 ==========
rows = []
total_amount = 0.0 # 应付合计
total_amount_actual = 0.0 # 实付合计
total_fee = 0.0 # 支付金额合计
for idx, rec in enumerate(records, start=1):
due_date_str = rec.due_date.strftime("%Y-%m-%d") if rec.due_date else ""
actual_date_str = rec.actual_date.strftime("%Y-%m-%d") if rec.actual_date else ""
amount_val = round(rec.amount or 0.0, 2)
amount_actual_val = round(rec.amount_actual or 0.0, 2)
total_amount += amount_val
total_amount_actual += amount_actual_val
pay_date_str = rec.pay_date.strftime("%Y-%m-%d") if rec.pay_date else ""
fee_val = round(rec.form_fee or 0.0, 2)
total_fee += fee_val
rows.append({
"序号": idx,
"应付日期": due_date_str,
"实付日期": actual_date_str,
"应付": amount_val,
"实付": amount_actual_val,
"标题": rec.name or "",
"所属公司": rec.company_id1.name if rec.company_id1 else "",
"支付金额": fee_val,
"申请人": rec.users_id.name if rec.users_id else "",
"支付日期": pay_date_str,
"权属类型": dict(rec._fields['tenure_type'].selection).get(rec.tenure_type, rec.tenure_type or ""),
})
# ========== 五、构建统计汇总数据 ==========
stats = [
{'name': '记录总数', 'amount': len(rows)},
{'name': '应付合计', 'amount': round(total_amount, 2)},
{'name': '实付合计', 'amount': round(total_amount_actual, 2)},
{'name': '支付合计', 'amount': round(total_fee, 2)},
]
_logger.info(
"[ASSESSMENT_FEE_MONTHLY] query_date=%s, month_range=[%s, %s], rows=%d, "
"total_amount=%.2f, total_amount_actual=%.2f",
query_date, month_first, month_last, len(rows), total_amount, total_amount_actual,
"[ASSESSMENT_FEE_MONTHLY] query_date=%s, month_range=[%s, %s], rows=%d, total_fee=%.2f",
query_date, month_first, month_last, len(rows), total_fee,
)
return {"rows": rows, "stats": stats}
@@ -8,18 +8,19 @@ from .report_utils import _id_list # 导入 ID 列表解析工具函数,支
_logger = logging.getLogger(__name__)
class PropertyBillsList(models.Model):
_inherit = 'property.bills.list'
class PropertyFeeForm(models.Model):
# 改为继承 property.fee.form 模型
_inherit = 'property.fee.form'
@api.model
def get_assessment_fee_yearly_report(self, params):
"""评估费本年累计报表。
数据源:property.bills.list
数据源:property.fee.form
过滤条件:
- management_type_id.name = '评估费'
- actual_date(实付日期)在查询月所在年的1月1日~查询月末
字段:应付日期、实付日期、应付、实付、标题
- expenses_type.name = '评估费'
- pay_date(支付日期)在查询月所在年的1月1日~查询月末
字段:标题、所属公司、支付金额、申请人、支付日期、权属类型
"""
# ========== 一、参数解析 ==========
date_start = params.get("date_start")
@@ -41,68 +42,61 @@ class PropertyBillsList(models.Model):
# ========== 三、构建domain ==========
domain = [
('management_type_id.name', '=', '评估费'),
('actual_date', '>=', year_start),
('actual_date', '<=', month_last),
('expenses_type.name', '=', '评估费'), # 支出类型为评估费
('pay_date', '>=', year_start), # 支付日期在年初及以后
('pay_date', '<=', month_last), # 支付日期在查询月末及以前
]
# 关键字过滤
# 关键字过滤:按标题模糊搜索
if keyword:
domain += ['|', '|', '|',
('name', 'ilike', keyword),
('contract_id.contract_code', 'ilike', keyword),
('lessee_id.name', 'ilike', keyword),
('property_name', 'ilike', keyword),
]
domain += ['|', ('name', 'ilike', keyword),
('users_id.name', 'ilike', keyword)]
# 公司/片区过滤
# 公司过滤:company_id1 为所属公司字段
company_id = _id_list(params.get('company_id'))
partner_id = _id_list(params.get('partner_id')) # 解析片区 ID 为整数列表(多选支持)
if company_id:
domain.append(('lessor_id', 'in', company_id))
domain.append(('company_id1', 'in', company_id))
# 片区(partner_id):通过 precinct.message -> employee -> user 关联申请人
if partner_id:
domain += ['|',
('contract_id.precinct_id', 'in', partner_id), # 合同片区过滤(多选支持)
('receivables_id.precinct_id', 'in', partner_id), # 应收记录片区过滤(多选支持)
]
precincts = self.env['yuthon.precinct.message'].sudo().browse(partner_id)
user_ids = precincts.mapped('employee_id.user_id').ids
if user_ids:
domain.append(('users_id', 'in', user_ids))
else:
domain.append(('id', '=', False))
# 查询记录
records = self.sudo().search(domain)
records = self.sudo().search(domain, order='pay_date asc')
# ========== 四、构建报表行 ==========
rows = []
total_amount = 0.0 # 应付合计
total_amount_actual = 0.0 # 实付合计
total_fee = 0.0 # 支付金额合计
for idx, rec in enumerate(records, start=1):
due_date_str = rec.due_date.strftime("%Y-%m-%d") if rec.due_date else ""
actual_date_str = rec.actual_date.strftime("%Y-%m-%d") if rec.actual_date else ""
amount_val = round(rec.amount or 0.0, 2)
amount_actual_val = round(rec.amount_actual or 0.0, 2)
total_amount += amount_val
total_amount_actual += amount_actual_val
pay_date_str = rec.pay_date.strftime("%Y-%m-%d") if rec.pay_date else ""
fee_val = round(rec.form_fee or 0.0, 2)
total_fee += fee_val
rows.append({
"序号": idx,
"应付日期": due_date_str,
"实付日期": actual_date_str,
"应付": amount_val,
"实付": amount_actual_val,
"标题": rec.name or "",
"所属公司": rec.company_id1.name if rec.company_id1 else "",
"支付金额": fee_val,
"申请人": rec.users_id.name if rec.users_id else "",
"支付日期": pay_date_str,
"权属类型": dict(rec._fields['tenure_type'].selection).get(rec.tenure_type, rec.tenure_type or ""),
})
# ========== 五、构建统计汇总数据 ==========
stats = [
{'name': '记录总数', 'amount': len(rows)},
{'name': '应付合计', 'amount': round(total_amount, 2)},
{'name': '实付合计', 'amount': round(total_amount_actual, 2)},
{'name': '支付合计', 'amount': round(total_fee, 2)},
]
_logger.info(
"[ASSESSMENT_FEE_YEARLY] query_date=%s, year_range=[%s, %s], rows=%d, "
"total_amount=%.2f, total_amount_actual=%.2f",
query_date, year_start, month_last, len(rows), total_amount, total_amount_actual,
"[ASSESSMENT_FEE_YEARLY] query_date=%s, year_range=[%s, %s], rows=%d, total_fee=%.2f",
query_date, year_start, month_last, len(rows), total_fee,
)
return {"rows": rows, "stats": stats}
@@ -8,18 +8,19 @@ from .report_utils import _id_list # 导入 ID 列表解析工具函数,支
_logger = logging.getLogger(__name__)
class PropertyBillsList(models.Model):
_inherit = 'property.bills.list'
class PropertyFeeForm(models.Model):
# 改为继承 property.fee.form 模型
_inherit = 'property.fee.form'
@api.model
def get_maintenance_fee_monthly_report(self, params):
"""维修费本月支出报表。
数据源:property.bills.list
数据源:property.fee.form
过滤条件:
- management_type_id.name = '维修费'
- actual_date(实付日期)在查询月本月范围内(月初~月末)
字段:应付日期、实付日期、应付、实付、标题
- expenses_type.name = '维修费'
- pay_date(支付日期)在查询月本月范围内(月初~月末)
字段:标题、所属公司、支付金额、申请人、支付日期、权属类型
"""
# ========== 一、参数解析 ==========
date_start = params.get("date_start")
@@ -41,68 +42,61 @@ class PropertyBillsList(models.Model):
# ========== 三、构建domain ==========
domain = [
('management_type_id.name', '=', '维修费'),
('actual_date', '>=', month_first),
('actual_date', '<=', month_last),
('expenses_type.name', '=', '维修费'), # 支出类型为维修费
('pay_date', '>=', month_first), # 支付日期在查询月月初及以后
('pay_date', '<=', month_last), # 支付日期在查询月月末及以前
]
# 关键字过滤
# 关键字过滤:按标题模糊搜索
if keyword:
domain += ['|', '|', '|',
('name', 'ilike', keyword),
('contract_id.contract_code', 'ilike', keyword),
('lessee_id.name', 'ilike', keyword),
('property_name', 'ilike', keyword),
]
domain += ['|', ('name', 'ilike', keyword),
('users_id.name', 'ilike', keyword)]
# 公司/片区过滤
# 公司过滤:company_id1 为所属公司字段
company_id = _id_list(params.get('company_id'))
partner_id = _id_list(params.get('partner_id')) # 解析片区 ID 为整数列表(多选支持)
if company_id:
domain.append(('lessor_id', 'in', company_id))
domain.append(('company_id1', 'in', company_id))
# 片区(partner_id):通过 precinct.message -> employee -> user 关联申请人
if partner_id:
domain += ['|',
('contract_id.precinct_id', 'in', partner_id), # 合同片区过滤(多选支持)
('receivables_id.precinct_id', 'in', partner_id), # 应收记录片区过滤(多选支持)
]
precincts = self.env['yuthon.precinct.message'].sudo().browse(partner_id)
user_ids = precincts.mapped('employee_id.user_id').ids
if user_ids:
domain.append(('users_id', 'in', user_ids))
else:
domain.append(('id', '=', False))
# 查询记录
records = self.sudo().search(domain)
records = self.sudo().search(domain, order='pay_date asc')
# ========== 四、构建报表行 ==========
rows = []
total_amount = 0.0 # 应付合计
total_amount_actual = 0.0 # 实付合计
total_fee = 0.0 # 支付金额合计
for idx, rec in enumerate(records, start=1):
due_date_str = rec.due_date.strftime("%Y-%m-%d") if rec.due_date else ""
actual_date_str = rec.actual_date.strftime("%Y-%m-%d") if rec.actual_date else ""
amount_val = round(rec.amount or 0.0, 2)
amount_actual_val = round(rec.amount_actual or 0.0, 2)
total_amount += amount_val
total_amount_actual += amount_actual_val
pay_date_str = rec.pay_date.strftime("%Y-%m-%d") if rec.pay_date else ""
fee_val = round(rec.form_fee or 0.0, 2)
total_fee += fee_val
rows.append({
"序号": idx,
"应付日期": due_date_str,
"实付日期": actual_date_str,
"应付": amount_val,
"实付": amount_actual_val,
"标题": rec.name or "",
"所属公司": rec.company_id1.name if rec.company_id1 else "",
"支付金额": fee_val,
"申请人": rec.users_id.name if rec.users_id else "",
"支付日期": pay_date_str,
"权属类型": dict(rec._fields['tenure_type'].selection).get(rec.tenure_type, rec.tenure_type or ""),
})
# ========== 五、构建统计汇总数据 ==========
stats = [
{'name': '记录总数', 'amount': len(rows)},
{'name': '应付合计', 'amount': round(total_amount, 2)},
{'name': '实付合计', 'amount': round(total_amount_actual, 2)},
{'name': '支付合计', 'amount': round(total_fee, 2)},
]
_logger.info(
"[MAINTENANCE_FEE_MONTHLY] query_date=%s, month_range=[%s, %s], rows=%d, "
"total_amount=%.2f, total_amount_actual=%.2f",
query_date, month_first, month_last, len(rows), total_amount, total_amount_actual,
"[MAINTENANCE_FEE_MONTHLY] query_date=%s, month_range=[%s, %s], rows=%d, total_fee=%.2f",
query_date, month_first, month_last, len(rows), total_fee,
)
return {"rows": rows, "stats": stats}
@@ -8,18 +8,19 @@ from .report_utils import _id_list # 导入 ID 列表解析工具函数,支
_logger = logging.getLogger(__name__)
class PropertyBillsList(models.Model):
_inherit = 'property.bills.list'
class PropertyFeeForm(models.Model):
# 改为继承 property.fee.form 模型
_inherit = 'property.fee.form'
@api.model
def get_maintenance_fee_yearly_report(self, params):
"""维修费本年累计报表。
数据源:property.bills.list
数据源:property.fee.form
过滤条件:
- management_type_id.name = '维修费'
- actual_date(实付日期)在查询月所在年的1月1日~查询月末
字段:应付日期、实付日期、应付、实付、标题
- expenses_type.name = '维修费'
- pay_date(支付日期)在查询月所在年的1月1日~查询月末
字段:标题、所属公司、支付金额、申请人、支付日期、权属类型
"""
# ========== 一、参数解析 ==========
date_start = params.get("date_start")
@@ -41,68 +42,61 @@ class PropertyBillsList(models.Model):
# ========== 三、构建domain ==========
domain = [
('management_type_id.name', '=', '维修费'),
('actual_date', '>=', year_start),
('actual_date', '<=', month_last),
('expenses_type.name', '=', '维修费'), # 支出类型为维修费
('pay_date', '>=', year_start), # 支付日期在年初及以后
('pay_date', '<=', month_last), # 支付日期在查询月末及以前
]
# 关键字过滤
# 关键字过滤:按标题模糊搜索
if keyword:
domain += ['|', '|', '|',
('name', 'ilike', keyword),
('contract_id.contract_code', 'ilike', keyword),
('lessee_id.name', 'ilike', keyword),
('property_name', 'ilike', keyword),
]
domain += ['|', ('name', 'ilike', keyword),
('users_id.name', 'ilike', keyword)]
# 公司/片区过滤
# 公司过滤:company_id1 为所属公司字段
company_id = _id_list(params.get('company_id'))
partner_id = _id_list(params.get('partner_id')) # 解析片区 ID 为整数列表(多选支持)
if company_id:
domain.append(('lessor_id', 'in', company_id))
domain.append(('company_id1', 'in', company_id))
# 片区(partner_id):通过 precinct.message -> employee -> user 关联申请人
if partner_id:
domain += ['|',
('contract_id.precinct_id', 'in', partner_id), # 合同片区过滤(多选支持)
('receivables_id.precinct_id', 'in', partner_id), # 应收记录片区过滤(多选支持)
]
precincts = self.env['yuthon.precinct.message'].sudo().browse(partner_id)
user_ids = precincts.mapped('employee_id.user_id').ids
if user_ids:
domain.append(('users_id', 'in', user_ids))
else:
domain.append(('id', '=', False))
# 查询记录
records = self.sudo().search(domain)
records = self.sudo().search(domain, order='pay_date asc')
# ========== 四、构建报表行 ==========
rows = []
total_amount = 0.0 # 应付合计
total_amount_actual = 0.0 # 实付合计
total_fee = 0.0 # 支付金额合计
for idx, rec in enumerate(records, start=1):
due_date_str = rec.due_date.strftime("%Y-%m-%d") if rec.due_date else ""
actual_date_str = rec.actual_date.strftime("%Y-%m-%d") if rec.actual_date else ""
amount_val = round(rec.amount or 0.0, 2)
amount_actual_val = round(rec.amount_actual or 0.0, 2)
total_amount += amount_val
total_amount_actual += amount_actual_val
pay_date_str = rec.pay_date.strftime("%Y-%m-%d") if rec.pay_date else ""
fee_val = round(rec.form_fee or 0.0, 2)
total_fee += fee_val
rows.append({
"序号": idx,
"应付日期": due_date_str,
"实付日期": actual_date_str,
"应付": amount_val,
"实付": amount_actual_val,
"标题": rec.name or "",
"所属公司": rec.company_id1.name if rec.company_id1 else "",
"支付金额": fee_val,
"申请人": rec.users_id.name if rec.users_id else "",
"支付日期": pay_date_str,
"权属类型": dict(rec._fields['tenure_type'].selection).get(rec.tenure_type, rec.tenure_type or ""),
})
# ========== 五、构建统计汇总数据 ==========
stats = [
{'name': '记录总数', 'amount': len(rows)},
{'name': '应付合计', 'amount': round(total_amount, 2)},
{'name': '实付合计', 'amount': round(total_amount_actual, 2)},
{'name': '支付合计', 'amount': round(total_fee, 2)},
]
_logger.info(
"[MAINTENANCE_FEE_YEARLY] query_date=%s, year_range=[%s, %s], rows=%d, "
"total_amount=%.2f, total_amount_actual=%.2f",
query_date, year_start, month_last, len(rows), total_amount, total_amount_actual,
"[MAINTENANCE_FEE_YEARLY] query_date=%s, year_range=[%s, %s], rows=%d, total_fee=%.2f",
query_date, year_start, month_last, len(rows), total_fee,
)
return {"rows": rows, "stats": stats}
@@ -8,18 +8,19 @@ from .report_utils import _id_list # 导入 ID 列表解析工具函数,支
_logger = logging.getLogger(__name__)
class PropertyBillsList(models.Model):
_inherit = 'property.bills.list'
class PropertyFeeForm(models.Model):
# 改为继承 property.fee.form 模型
_inherit = 'property.fee.form'
@api.model
def get_management_fee_monthly_report(self, params):
"""管理费本月支出报表。
数据源:property.bills.list
数据源:property.fee.form
过滤条件:
- management_type_id.name = '管理费'
- actual_date(实付日期)在查询月本月范围内(月初~月末)
字段:应付日期、实付日期、应付、实付、标题
- expenses_type.name = '管理费'
- pay_date(支付日期)在查询月本月范围内(月初~月末)
字段:标题、所属公司、支付金额、申请人、支付日期、权属类型
"""
# ========== 一、参数解析 ==========
date_start = params.get("date_start")
@@ -41,68 +42,63 @@ class PropertyBillsList(models.Model):
# ========== 三、构建domain ==========
domain = [
('management_type_id.name', '=', '管理费'),
('actual_date', '>=', month_first),
('actual_date', '<=', month_last),
('expenses_type.name', '=', '管理费'), # 支出类型为管理费
('pay_date', '>=', month_first), # 支付日期在查询月月初及以后
('pay_date', '<=', month_last), # 支付日期在查询月月末及以前
]
# 关键字过滤
# 关键字过滤:按标题模糊搜索
if keyword:
domain += ['|', '|', '|',
('name', 'ilike', keyword),
('contract_id.contract_code', 'ilike', keyword),
('lessee_id.name', 'ilike', keyword),
('property_name', 'ilike', keyword),
]
domain += ['|', ('name', 'ilike', keyword),
('users_id.name', 'ilike', keyword)]
# 公司/片区过滤
# 公司过滤:company_id1 为所属公司字段
company_id = _id_list(params.get('company_id'))
partner_id = _id_list(params.get('partner_id')) # 解析片区 ID 为整数列表(多选支持)
if company_id:
domain.append(('lessor_id', 'in', company_id))
domain.append(('company_id1', 'in', company_id))
# 片区(partner_id):通过 precinct.message -> employee -> user 关联申请人
if partner_id:
domain += ['|',
('contract_id.precinct_id', 'in', partner_id), # 合同片区过滤(多选支持)
('receivables_id.precinct_id', 'in', partner_id), # 应收记录片区过滤(多选支持)
]
# partner_id 是 yuthon.precinct.message 的 ID,需转换为 res.users ID
precincts = self.env['yuthon.precinct.message'].sudo().browse(partner_id)
user_ids = precincts.mapped('employee_id.user_id').ids
if user_ids:
domain.append(('users_id', 'in', user_ids))
else:
# 片区没有关联到任何用户,返回空结果
domain.append(('id', '=', False))
# 查询记录
records = self.sudo().search(domain)
records = self.sudo().search(domain, order='pay_date asc')
# ========== 四、构建报表行 ==========
rows = []
total_amount = 0.0 # 应付合计
total_amount_actual = 0.0 # 实付合计
total_fee = 0.0 # 支付金额合计
for idx, rec in enumerate(records, start=1):
due_date_str = rec.due_date.strftime("%Y-%m-%d") if rec.due_date else ""
actual_date_str = rec.actual_date.strftime("%Y-%m-%d") if rec.actual_date else ""
amount_val = round(rec.amount or 0.0, 2)
amount_actual_val = round(rec.amount_actual or 0.0, 2)
total_amount += amount_val
total_amount_actual += amount_actual_val
pay_date_str = rec.pay_date.strftime("%Y-%m-%d") if rec.pay_date else ""
fee_val = round(rec.form_fee or 0.0, 2)
total_fee += fee_val
rows.append({
"序号": idx,
"应付日期": due_date_str,
"实付日期": actual_date_str,
"应付": amount_val,
"实付": amount_actual_val,
"标题": rec.name or "",
"所属公司": rec.company_id1.name if rec.company_id1 else "",
"支付金额": fee_val,
"申请人": rec.users_id.name if rec.users_id else "",
"支付日期": pay_date_str,
"权属类型": dict(rec._fields['tenure_type'].selection).get(rec.tenure_type, rec.tenure_type or ""),
})
# ========== 五、构建统计汇总数据 ==========
stats = [
{'name': '记录总数', 'amount': len(rows)},
{'name': '应付合计', 'amount': round(total_amount, 2)},
{'name': '实付合计', 'amount': round(total_amount_actual, 2)},
{'name': '支付合计', 'amount': round(total_fee, 2)},
]
_logger.info(
"[MANAGEMENT_FEE_MONTHLY] query_date=%s, month_range=[%s, %s], rows=%d, "
"total_amount=%.2f, total_amount_actual=%.2f",
query_date, month_first, month_last, len(rows), total_amount, total_amount_actual,
"[MANAGEMENT_FEE_MONTHLY] query_date=%s, month_range=[%s, %s], rows=%d, total_fee=%.2f",
query_date, month_first, month_last, len(rows), total_fee,
)
return {"rows": rows, "stats": stats}
@@ -8,18 +8,19 @@ from .report_utils import _id_list # 导入 ID 列表解析工具函数,支
_logger = logging.getLogger(__name__)
class PropertyBillsList(models.Model):
_inherit = 'property.bills.list'
class PropertyFeeForm(models.Model):
# 改为继承 property.fee.form 模型
_inherit = 'property.fee.form'
@api.model
def get_management_fee_yearly_report(self, params):
"""管理费本年累计报表。
数据源:property.bills.list
数据源:property.fee.form
过滤条件:
- management_type_id.name = '管理费'
- actual_date(实付日期)在查询月所在年的1月1日~查询月末
字段:应付日期、实付日期、应付、实付、标题
- expenses_type.name = '管理费'
- pay_date(支付日期)在查询月所在年的1月1日~查询月末
字段:标题、所属公司、支付金额、申请人、支付日期、权属类型
"""
# ========== 一、参数解析 ==========
date_start = params.get("date_start")
@@ -41,68 +42,61 @@ class PropertyBillsList(models.Model):
# ========== 三、构建domain ==========
domain = [
('management_type_id.name', '=', '管理费'),
('actual_date', '>=', year_start),
('actual_date', '<=', month_last),
('expenses_type.name', '=', '管理费'), # 支出类型为管理费
('pay_date', '>=', year_start), # 支付日期在年初及以后
('pay_date', '<=', month_last), # 支付日期在查询月末及以前
]
# 关键字过滤
# 关键字过滤:按标题模糊搜索
if keyword:
domain += ['|', '|', '|',
('name', 'ilike', keyword),
('contract_id.contract_code', 'ilike', keyword),
('lessee_id.name', 'ilike', keyword),
('property_name', 'ilike', keyword),
]
domain += ['|', ('name', 'ilike', keyword),
('users_id.name', 'ilike', keyword)]
# 公司/片区过滤
# 公司过滤:company_id1 为所属公司字段
company_id = _id_list(params.get('company_id'))
partner_id = _id_list(params.get('partner_id')) # 解析片区 ID 为整数列表(多选支持)
if company_id:
domain.append(('lessor_id', 'in', company_id))
domain.append(('company_id1', 'in', company_id))
# 片区(partner_id):通过 precinct.message -> employee -> user 关联申请人
if partner_id:
domain += ['|',
('contract_id.precinct_id', 'in', partner_id), # 合同片区过滤(多选支持)
('receivables_id.precinct_id', 'in', partner_id), # 应收记录片区过滤(多选支持)
]
precincts = self.env['yuthon.precinct.message'].sudo().browse(partner_id)
user_ids = precincts.mapped('employee_id.user_id').ids
if user_ids:
domain.append(('users_id', 'in', user_ids))
else:
domain.append(('id', '=', False))
# 查询记录
records = self.sudo().search(domain)
records = self.sudo().search(domain, order='pay_date asc')
# ========== 四、构建报表行 ==========
rows = []
total_amount = 0.0 # 应付合计
total_amount_actual = 0.0 # 实付合计
total_fee = 0.0 # 支付金额合计
for idx, rec in enumerate(records, start=1):
due_date_str = rec.due_date.strftime("%Y-%m-%d") if rec.due_date else ""
actual_date_str = rec.actual_date.strftime("%Y-%m-%d") if rec.actual_date else ""
amount_val = round(rec.amount or 0.0, 2)
amount_actual_val = round(rec.amount_actual or 0.0, 2)
total_amount += amount_val
total_amount_actual += amount_actual_val
pay_date_str = rec.pay_date.strftime("%Y-%m-%d") if rec.pay_date else ""
fee_val = round(rec.form_fee or 0.0, 2)
total_fee += fee_val
rows.append({
"序号": idx,
"应付日期": due_date_str,
"实付日期": actual_date_str,
"应付": amount_val,
"实付": amount_actual_val,
"标题": rec.name or "",
"所属公司": rec.company_id1.name if rec.company_id1 else "",
"支付金额": fee_val,
"申请人": rec.users_id.name if rec.users_id else "",
"支付日期": pay_date_str,
"权属类型": dict(rec._fields['tenure_type'].selection).get(rec.tenure_type, rec.tenure_type or ""),
})
# ========== 五、构建统计汇总数据 ==========
stats = [
{'name': '记录总数', 'amount': len(rows)},
{'name': '应付合计', 'amount': round(total_amount, 2)},
{'name': '实付合计', 'amount': round(total_amount_actual, 2)},
{'name': '支付合计', 'amount': round(total_fee, 2)},
]
_logger.info(
"[MANAGEMENT_FEE_YEARLY] query_date=%s, year_range=[%s, %s], rows=%d, "
"total_amount=%.2f, total_amount_actual=%.2f",
query_date, year_start, month_last, len(rows), total_amount, total_amount_actual,
"[MANAGEMENT_FEE_YEARLY] query_date=%s, year_range=[%s, %s], rows=%d, total_fee=%.2f",
query_date, year_start, month_last, len(rows), total_fee,
)
return {"rows": rows, "stats": stats}
+42 -36
View File
@@ -313,18 +313,19 @@ class YuthonDashboard(models.Model):
amount = row.get('金额', 0)
monthly_precollection.append({'text': f"{idx}. {prop_name}本月预收:{self._format_amount(amount)}元。"})
# 支出:调用三个费用报表方法,从 rows 取标题和实付金额
# 支出:调用三个费用报表方法,从 rows 取标题和支付金额
# 数据源已改为 property.fee.form,金额字段为 支付金额
expense = []
fee_methods = [
('管理费', 'property.bills.list', 'get_management_fee_monthly_report'),
('维修费', 'property.bills.list', 'get_maintenance_fee_monthly_report'),
('评估费', 'property.bills.list', 'get_assessment_fee_monthly_report'),
('管理费', 'property.fee.form', 'get_management_fee_monthly_report'),
('维修费', 'property.fee.form', 'get_maintenance_fee_monthly_report'),
('评估费', 'property.fee.form', 'get_assessment_fee_monthly_report'),
]
for fee_label, model_name, method_name in fee_methods:
fee_result = getattr(self.env[model_name], method_name)(base_params)
for row in fee_result.get('rows', []):
title = row.get('标题', '')
amount_actual = row.get('实付', 0)
amount_actual = row.get('支付金额', 0) # 新模型金额字段为 支付金额
if amount_actual > 0:
expense.append(f"{title}{fee_label}支出{self._format_amount(amount_actual)}元。")
@@ -392,18 +393,18 @@ class YuthonDashboard(models.Model):
return '未知'
def _get_fee_expense_data(self, base_params, query_date):
"""费用支出:管理费、维修费、评估费及其它"""
"""费用支出:管理费、维修费、评估费及其它(数据源改为 property.fee.form)"""
try:
month_first = query_date.replace(day=1)
last_day = calendar.monthrange(query_date.year, query_date.month)[1]
month_last = query_date.replace(day=last_day)
company_id = _id_list(base_params.get('company_id'))
# 多选标准化:partner_id 统一为列表
# 多选标准化:partner_id 统一为列表(fee.form 无片区关联,暂保留以备扩展)
partner_id = _id_list(base_params.get('partner_id'))
keyword = base_params.get('keyword')
# 按费用类型分别统计(数据源:property.bills.list)
# 按费用类型分别统计(数据源:property.fee.form)
fee_types = ['管理费', '维修费', '评估费']
cards = []
@@ -411,47 +412,52 @@ class YuthonDashboard(models.Model):
row_labels_explanations = []
for label in fee_types:
# 费用支出数据来源:property.bills.list
# 费用支出数据来源:property.fee.form
# expenses_type.name = 费用类型,pay_date = 支付日期,form_fee = 支付金额
domain = [
('management_type_id.name', '=', label),
('actual_date', '>=', month_first),
('actual_date', '<=', month_last),
('expenses_type.name', '=', label), # 支出类型匹配
('pay_date', '>=', month_first), # 支付日期在查询月月初及以后
('pay_date', '<=', month_last), # 支付日期在查询月月末及以前
]
if company_id:
domain.append(('lessor_id', 'in', company_id))
domain.append(('company_id1', 'in', company_id)) # 所属公司字段为 company_id1
# 片区过滤:partner_id 是 precinct.message ID,需转换为 res.users ID
if partner_id:
# 多选:片区管理员用 'in' 操作符
domain += ['|',
('contract_id.precinct_id', 'in', partner_id),
('receivables_id.precinct_id', 'in', partner_id),
]
precincts = self.env['yuthon.precinct.message'].sudo().browse(partner_id)
user_ids = precincts.mapped('employee_id.user_id').ids
if user_ids:
domain.append(('users_id', 'in', user_ids))
else:
domain.append(('id', '=', False))
if keyword:
domain += ['|', '|', '|',
('name', 'ilike', keyword),
('contract_id.contract_code', 'ilike', keyword),
('lessee_id.name', 'ilike', keyword),
('property_name', 'ilike', keyword)]
domain += ['|', ('name', 'ilike', keyword),
('users_id.name', 'ilike', keyword)]
records = self.env['property.bills.list'].sudo().search(domain)
month_total = sum((r.amount_actual or 0.0) for r in records)
records = self.env['property.fee.form'].sudo().search(domain)
month_total = sum((r.form_fee or 0.0) for r in records) # 支付金额字段为 form_fee
# 本年累计
year_start = datetime.date(query_date.year, 1, 1)
year_domain = [
('management_type_id.name', '=', label),
('actual_date', '>=', year_start),
('actual_date', '<=', month_last),
('expenses_type.name', '=', label), # 支出类型匹配
('pay_date', '>=', year_start), # 支付日期在年初及以后
('pay_date', '<=', month_last), # 支付日期在查询月末及以前
]
if company_id:
year_domain.append(('lessor_id', 'in', company_id))
year_domain.append(('company_id1', 'in', company_id))
# 片区过滤:partner_id 是 precinct.message ID,需转换为 res.users ID
if partner_id:
# 多选:片区管理员用 'in' 操作符
year_domain += ['|',
('contract_id.precinct_id', 'in', partner_id),
('receivables_id.precinct_id', 'in', partner_id),
]
year_records = self.env['property.bills.list'].sudo().search(year_domain)
year_total = sum((r.amount_actual or 0.0) for r in year_records)
precincts = self.env['yuthon.precinct.message'].sudo().browse(partner_id)
user_ids = precincts.mapped('employee_id.user_id').ids
if user_ids:
year_domain.append(('users_id', 'in', user_ids))
else:
year_domain.append(('id', '=', False))
if keyword:
year_domain += ['|', ('name', 'ilike', keyword),
('users_id.name', 'ilike', keyword)]
year_records = self.env['property.fee.form'].sudo().search(year_domain)
year_total = sum((r.form_fee or 0.0) for r in year_records)
# 本月支出卡片:管理费、维修费绑定报表,其他费用暂不绑定
if label == '管理费':
@@ -8,16 +8,17 @@
{
"sideBar": true,
"report_config": {
"model": "property.bills.list",
"model": "property.fee.form",
"method": "get_assessment_fee_monthly_report",
"dateMode": "single",
"columns": [
{"headerName": "序号", "field": "序号", "width": 45, "flex": 0, "pinned": "left"},
{"headerName": "应付日期", "field": "应付日期", "flex": 1.2},
{"headerName": "实付日期", "field": "实付日期", "flex": 1.2},
{"headerName": "应付", "field": "应付", "flex": 1, "type": "number"},
{"headerName": "实付", "field": "实付", "flex": 1, "type": "number"},
{"headerName": "标题", "field": "标题", "flex": 2.5}
{"headerName": "支付日期", "field": "支付日期", "flex": 1.2},
{"headerName": "所属公司", "field": "所属公司", "flex": 1.2},
{"headerName": "标题", "field": "标题", "flex": 2.5},
{"headerName": "支付金额", "field": "支付金额", "flex": 1, "type": "number"},
{"headerName": "申请人", "field": "申请人", "flex": 1},
{"headerName": "权属类型", "field": "权属类型", "flex": 1}
]
}
}
@@ -8,16 +8,17 @@
{
"sideBar": true,
"report_config": {
"model": "property.bills.list",
"model": "property.fee.form",
"method": "get_assessment_fee_yearly_report",
"dateMode": "single",
"columns": [
{"headerName": "序号", "field": "序号", "width": 45, "flex": 0, "pinned": "left"},
{"headerName": "应付日期", "field": "应付日期", "flex": 1.2},
{"headerName": "实付日期", "field": "实付日期", "flex": 1.2},
{"headerName": "应付", "field": "应付", "flex": 1, "type": "number"},
{"headerName": "实付", "field": "实付", "flex": 1, "type": "number"},
{"headerName": "标题", "field": "标题", "flex": 2.5}
{"headerName": "支付日期", "field": "支付日期", "flex": 1.2},
{"headerName": "所属公司", "field": "所属公司", "flex": 1.2},
{"headerName": "标题", "field": "标题", "flex": 2.5},
{"headerName": "支付金额", "field": "支付金额", "flex": 1, "type": "number"},
{"headerName": "申请人", "field": "申请人", "flex": 1},
{"headerName": "权属类型", "field": "权属类型", "flex": 1}
]
}
}
@@ -8,16 +8,17 @@
{
"sideBar": true,
"report_config": {
"model": "property.bills.list",
"model": "property.fee.form",
"method": "get_maintenance_fee_monthly_report",
"dateMode": "single",
"columns": [
{"headerName": "序号", "field": "序号", "width": 45, "flex": 0, "pinned": "left"},
{"headerName": "应付日期", "field": "应付日期", "flex": 1.2},
{"headerName": "实付日期", "field": "实付日期", "flex": 1.2},
{"headerName": "应付", "field": "应付", "flex": 1, "type": "number"},
{"headerName": "实付", "field": "实付", "flex": 1, "type": "number"},
{"headerName": "标题", "field": "标题", "flex": 2.5}
{"headerName": "支付日期", "field": "支付日期", "flex": 1.2},
{"headerName": "所属公司", "field": "所属公司", "flex": 1.2},
{"headerName": "标题", "field": "标题", "flex": 2.5},
{"headerName": "支付金额", "field": "支付金额", "flex": 1, "type": "number"},
{"headerName": "申请人", "field": "申请人", "flex": 1},
{"headerName": "权属类型", "field": "权属类型", "flex": 1}
]
}
}
@@ -8,16 +8,17 @@
{
"sideBar": true,
"report_config": {
"model": "property.bills.list",
"model": "property.fee.form",
"method": "get_maintenance_fee_yearly_report",
"dateMode": "single",
"columns": [
{"headerName": "序号", "field": "序号", "width": 45, "flex": 0, "pinned": "left"},
{"headerName": "应付日期", "field": "应付日期", "flex": 1.2},
{"headerName": "实付日期", "field": "实付日期", "flex": 1.2},
{"headerName": "应付", "field": "应付", "flex": 1, "type": "number"},
{"headerName": "实付", "field": "实付", "flex": 1, "type": "number"},
{"headerName": "标题", "field": "标题", "flex": 2.5}
{"headerName": "支付日期", "field": "支付日期", "flex": 1.2},
{"headerName": "所属公司", "field": "所属公司", "flex": 1.2},
{"headerName": "标题", "field": "标题", "flex": 2.5},
{"headerName": "支付金额", "field": "支付金额", "flex": 1, "type": "number"},
{"headerName": "申请人", "field": "申请人", "flex": 1},
{"headerName": "权属类型", "field": "权属类型", "flex": 1}
]
}
}
@@ -8,16 +8,17 @@
{
"sideBar": true,
"report_config": {
"model": "property.bills.list",
"model": "property.fee.form",
"method": "get_management_fee_monthly_report",
"dateMode": "single",
"columns": [
{"headerName": "序号", "field": "序号", "width": 45, "flex": 0, "pinned": "left"},
{"headerName": "应付日期", "field": "应付日期", "flex": 1.2},
{"headerName": "实付日期", "field": "实付日期", "flex": 1.2},
{"headerName": "应付", "field": "应付", "flex": 1, "type": "number"},
{"headerName": "实付", "field": "实付", "flex": 1, "type": "number"},
{"headerName": "标题", "field": "标题", "flex": 2.5}
{"headerName": "支付日期", "field": "支付日期", "flex": 1.2},
{"headerName": "所属公司", "field": "所属公司", "flex": 1.2},
{"headerName": "标题", "field": "标题", "flex": 2.5},
{"headerName": "支付金额", "field": "支付金额", "flex": 1, "type": "number"},
{"headerName": "申请人", "field": "申请人", "flex": 1},
{"headerName": "权属类型", "field": "权属类型", "flex": 1}
]
}
}
@@ -8,16 +8,17 @@
{
"sideBar": true,
"report_config": {
"model": "property.bills.list",
"model": "property.fee.form",
"method": "get_management_fee_yearly_report",
"dateMode": "single",
"columns": [
{"headerName": "序号", "field": "序号", "width": 45, "flex": 0, "pinned": "left"},
{"headerName": "应付日期", "field": "应付日期", "flex": 1.2},
{"headerName": "实付日期", "field": "实付日期", "flex": 1.2},
{"headerName": "应付", "field": "应付", "flex": 1, "type": "number"},
{"headerName": "实付", "field": "实付", "flex": 1, "type": "number"},
{"headerName": "标题", "field": "标题", "flex": 2.5}
{"headerName": "支付日期", "field": "支付日期", "flex": 1.2},
{"headerName": "所属公司", "field": "所属公司", "flex": 1.2},
{"headerName": "标题", "field": "标题", "flex": 2.5},
{"headerName": "支付金额", "field": "支付金额", "flex": 1, "type": "number"},
{"headerName": "申请人", "field": "申请人", "flex": 1},
{"headerName": "权属类型", "field": "权属类型", "flex": 1}
]
}
}