This commit is contained in:
李鹏宇
2026-03-17 18:22:13 +08:00
parent 92e982f316
commit cfb5f7ceaa
10 changed files with 357 additions and 63 deletions
+2
View File
@@ -16,6 +16,8 @@
'views/penalty_month_received_report.xml',
'views/penalty_year_cum_report.xml',
'views/deposit_reports.xml',
'views/cum_income_report.xml',
'views/cum_arrears_report.xml',
'views/menu.xml',
'views/ledger_report_views.xml',
'views/receivable_report_views.xml',
+2
View File
@@ -18,6 +18,8 @@ from . import pre_arrears_report
from . import pre_advance_report
from . import month_receivable_report
from . import total_received_report
from . import cum_income_report
from . import cum_arrears_report
from . import penalty_arrears_report
from . import penalty_month_received_report
from . import penalty_year_cum_report
+112
View File
@@ -0,0 +1,112 @@
import datetime
import calendar
from odoo import api, models
class CumArrearsReport(models.Model):
_inherit = 'receivable.modify.line'
@api.model
def get_cum_arrears_report(self, params):
"""
累计欠收报表(数据源:receivable.modify.line):
- 状态为挂账(bank_state = 'registered')
- 应收开始日期(receivable_id.due_date)小于等于查询月月末
- 排除押金和违约金类型
- 金额取 not_collected(未收)
"""
today = datetime.date.today()
date_start = params.get("date_start")
if date_start:
query_date = datetime.datetime.fromisoformat(date_start).date()
else:
query_date = today
# 查询月月末
last_day = calendar.monthrange(query_date.year, query_date.month)[1]
month_end = query_date.replace(day=last_day)
domain = [
('bank_state', '=', 'registered'),
('receivable_id.due_date', '<=', month_end),
('receivable_id.is_invalid', '=', False),
]
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),
]
condition = params.get('condition')
partner_id = params.get('partner_id')
company_id = params.get('company_id')
management_type_id = params.get('management_type_id')
if condition:
domain.append(('receivable_id.tenure_type', '=', condition))
if partner_id:
domain.append(('receivable_id.precinct_id', '=', partner_id))
if company_id:
domain.append(('receivable_id.company_id', '=', company_id))
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', ['押金', '违约金']))
lines = self.search(domain, order="receivable_id asc")
rows = []
total_amount = 0.0
for idx, line in enumerate(lines, start=1):
rec = line.receivable_id
contract = rec.contract_id if rec else None
amount = round(line.not_collected or 0.0, 2)
total_amount += amount
# 物业穿透
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 ''
rows.append({
"序号": idx,
"合同编号": {
'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 and rec.lessee_id else "",
"收款类型": rec.management_type_id.name if rec and rec.management_type_id else "",
"未收金额": amount,
"所属公司": contract.lessor_id.name if contract and contract.lessor_id else "",
"管理员": {
'model': 'res.users',
'id': rec.admin_id.id,
'display_name': rec.admin_id.name,
} if rec and rec.admin_id else "",
})
stats = [
{'name': '记录总数', 'amount': len(rows)},
{'name': '累计欠收', 'amount': round(total_amount, 2)},
]
return {"rows": rows, "stats": stats}
+105
View File
@@ -0,0 +1,105 @@
import datetime
from odoo import api, models
class CumIncomeReport(models.Model):
_inherit = 'receivable.modify.line'
@api.model
def get_cum_income_report(self, params):
"""
累计收入报表(数据源:receivable.modify.line):
- 按年查询
- 取交易金额(original_amount)
- 排除押金和违约金类型
"""
today = datetime.date.today()
current_year = today.year
date_start = params.get("date_start")
# 按年查询:如果有传值则使用该年份,否则使用当前年份
if date_start:
year = datetime.datetime.fromisoformat(date_start).year
else:
year = current_year
# 构建年份的开始和结束日期
year_start = datetime.date(year, 1, 1)
year_end = datetime.date(year, 12, 31)
domain = [
('receivable_id.due_date', '>=', year_start),
('receivable_id.due_date', '<=', year_end),
('receivable_id.is_invalid', '=', False),
]
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),
]
condition = params.get('condition')
partner_id = params.get('partner_id')
company_id = params.get('company_id')
management_type_id = params.get('management_type_id')
if condition:
domain.append(('receivable_id.tenure_type', '=', condition))
if partner_id:
domain.append(('receivable_id.precinct_id', '=', partner_id))
if company_id:
domain.append(('receivable_id.company_id', '=', company_id))
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', ['押金', '违约金']))
lines = self.search(domain, order="receivable_id asc")
rows = []
total_amount = 0.0
for idx, line in enumerate(lines, start=1):
rec = line.receivable_id
contract = rec.contract_id if rec else None
amount = round(line.original_amount or 0.0, 2)
total_amount += amount
# 物业穿透
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 ''
rows.append({
"序号": idx,
"合同编号": {
'model': 'property.lease.contract',
'id': contract.id,
'display_name': contract.contract_code,
} if contract else "",
"承租方名称": {
'model': 'property.tenant.information',
'id': rec.lessee_id.id,
'display_name': rec.lessee_id.name,
} if rec and rec.lessee_id else "",
"物业名称": property_val,
"金额": amount,
})
stats = [
{'name': '记录总数', 'amount': len(rows)},
{'name': '累计收入', 'amount': round(total_amount, 2)},
]
return {"rows": rows, "stats": stats}
@@ -1,90 +1,121 @@
import datetime
import calendar
from odoo import api, models
class MonthlyPrecollectionReport(models.Model):
_inherit = 'property.accounts.receivable'
_inherit = 'receivable.modify.line'
@api.model
def get_monthly_precollection_report(self, params):
"""汇总当前筛选条件下的应收数据(本月预收视图)。"""
domain = []
"""
本月预收报表(数据源:receivable.modify.line):
- 应收开始日期(receivable_id.due_date)在查询月当月(月初 ~ 月末)
- delivered1 > original_amount(已交超过当期金额,即有预收)
- 应收账单非作废:receivable_id.is_invalid = False
- 排除押金和违约金类型
- 金额取 delivered1 - original_amount(预收金额)
"""
today = datetime.date.today()
date_start = params.get("date_start")
if date_start:
query_date = datetime.datetime.fromisoformat(date_start).date()
else:
query_date = today
# 查询月月初和月末
month_start = query_date.replace(day=1)
last_day = calendar.monthrange(query_date.year, query_date.month)[1]
month_end = query_date.replace(day=last_day)
domain = [
('receivable_id.due_date', '>=', month_start),
('receivable_id.due_date', '<=', month_end),
('receivable_id.delivered1', '>', 0),
('receivable_id.is_invalid', '=', False),
]
keyword = params.get("keyword")
if keyword:
domain += [
'|', '|',
('lessee_id.name', 'ilike', keyword),
('property_name', 'ilike', keyword),
('title_deed', 'ilike', keyword),
('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")
if date_start:
start_dt = datetime.datetime.fromisoformat(date_start).date()
domain.append(("due_date", ">=", start_dt))
if date_end:
end_dt = datetime.datetime.fromisoformat(date_end).date()
domain.append(("due_date", "<=", end_dt))
condition = params.get('condition')
partner_id = params.get('partner_id')
company_id = params.get('company_id')
management_type_id = params.get('management_type_id')
if condition:
domain.append(('tenure_type', '=', condition))
domain.append(('receivable_id.tenure_type', '=', condition))
if partner_id:
domain.append(('precinct_id', '=', partner_id))
domain.append(('receivable_id.precinct_id', '=', partner_id))
if company_id:
domain.append(('company_id', '=', company_id))
domain.append(('receivable_id.company_id', '=', company_id))
if management_type_id:
domain.append(('management_type_id', '=', management_type_id))
domain.append(('receivable_id.management_type_id', '=', management_type_id))
else:
domain.append(('receivable_id.management_type_id.name', 'not in', ['押金', '违约金']))
receivables = self.search(domain, limit=1000, order="due_date asc")
lines = self.search(domain, order="receivable_id asc")
result = []
rows = []
total_amount = 0.0
for index, rec in enumerate(receivables, start=1):
amount = rec.original_amount or 0.0
idx = 1
for line in lines:
rec = line.receivable_id
contract = rec.contract_id if rec else None
# 预收金额 = 已交 - 当期金额,必须 > 0 才是真正预收
amount = round((rec.delivered1 or 0.0) - (rec.original_amount or 0.0), 2)
if amount <= 0:
continue
total_amount += amount
result.append({
"序号": index,
# 物业穿透
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 ''
rows.append({
"序号": idx,
"合同编号": {
'model': 'property.lease.contract',
'id': rec.contract_id.id,
'display_name': rec.contract_id.contract_code,
} if rec.contract_id else "",
"物业名称": {
'model': 'yuthon.property',
'id': rec.contract_id.lease_ids[0].address_id.id,
'display_name': rec.property_name,
} if rec.contract_id and rec.contract_id.lease_ids and rec.contract_id.lease_ids[0].address_id else rec.property_name or "",
'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 "",
"金额": round(amount, 2),
"所属公司": (
rec.contract_id.lessor_id.referred
or rec.contract_id.lessor_id.name
if rec.contract_id and rec.contract_id.lessor_id
else ""
),
} if rec and rec.lessee_id else "",
"收款类型": rec.management_type_id.name if rec and rec.management_type_id else "",
"金额": amount,
"所属公司": contract.lessor_id.name if contract and contract.lessor_id else "",
"管理员": {
'model': 'res.users',
'id': rec.contract_id.admin_id.id,
'display_name': rec.contract_id.admin_id.name,
} if rec.contract_id and rec.contract_id.admin_id else "",
'id': rec.admin_id.id,
'display_name': rec.admin_id.name,
} if rec and rec.admin_id else "",
})
idx += 1
return {
"rows": result,
"stats": [
{"name": "应收总额", "amount": round(total_amount, 2)},
],
}
stats = [
{'name': '合同总数', 'amount': len(rows)},
{'name': '预收总额', 'amount': round(total_amount, 2)},
]
return {"rows": rows, "stats": stats}
+10 -7
View File
@@ -11,11 +11,10 @@ class PreAdvanceReport(models.Model):
"""
前期预收报表(数据源:receivable.modify.line):
- 应收开始日期(receivable_id.due_date)在查询月最后一天之后(未来应收)
- 应收账单未收金额(receivable_id.submitted)= 0(已全额收款)
- original_amount > 0(有交易金额)
- delivered1 > original_amount(已交超过当期金额,即有预收)
- 应收账单非作废:receivable_id.is_invalid = False
- 排除押金和违约金类型
- 金额取 original_amount(交易金额)
- 金额取 delivered1 - original_amount(预收金额)
"""
today = datetime.date.today()
if today.month == 12:
@@ -30,8 +29,7 @@ class PreAdvanceReport(models.Model):
domain = [
('receivable_id.due_date', '>', cutoff_date),
('receivable_id.submitted', '=', 0),
('receivable_id.original_amount', '>', 0),
('receivable_id.delivered1', '>', 0),
('receivable_id.is_invalid', '=', False),
]
@@ -64,10 +62,14 @@ class PreAdvanceReport(models.Model):
rows = []
total_amount = 0.0
for idx, line in enumerate(lines, start=1):
idx = 1
for line in lines:
rec = line.receivable_id
contract = rec.contract_id if rec else None
amount = round(rec.original_amount or 0.0, 2)
# 预收金额 = 已交 - 当期金额,必须 > 0 才是真正预收
amount = round((rec.delivered1 or 0.0) - (rec.original_amount or 0.0), 2)
if amount <= 0:
continue
total_amount += amount
# 物业穿透
@@ -103,6 +105,7 @@ class PreAdvanceReport(models.Model):
'display_name': rec.admin_id.name,
} if rec and rec.admin_id else "",
})
idx += 1
stats = [
{'name': '合同总数', 'amount': len(rows)},
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="action_cum_arrears_report" model="ir.actions.client">
<field name="name">累计欠收</field>
<field name="tag">receivable_report_client</field>
<field name="context">
{
"sideBar": true,
"report_config": {
"model": "receivable.modify.line",
"method": "get_cum_arrears_report",
"dateMode": "single",
"columns": [
{"headerName": "序号", "field": "序号", "width": 45, "flex": 0, "pinned": "left"},
{"headerName": "合同编号", "field": "合同编号", "flex": 1.2},
{"headerName": "物业名称", "field": "物业名称", "flex": 2.5},
{"headerName": "承租方名称", "field": "承租方名称", "flex": 1.4},
{"headerName": "收款类型", "field": "收款类型", "flex": 1},
{"headerName": "未收金额", "field": "未收金额", "flex": 1, "type": "number"},
{"headerName": "所属公司", "field": "所属公司", "flex": 1},
{"headerName": "管理员", "field": "管理员", "flex": 1}
]
}
}
</field>
</record>
</data>
</odoo>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="action_cum_income_report" model="ir.actions.client">
<field name="name">累计收入</field>
<field name="tag">receivable_report_client</field>
<field name="context">{"report_config": {"model": "receivable.modify.line", "method": "get_cum_income_report", "columns": [{"headerName": "序号", "field": "序号", "width": 80, "flex": 0}, {"headerName": "合同编号", "field": "合同编号", "flex": 1}, {"headerName": "承租方名称", "field": "承租方名称", "flex": 1}, {"headerName": "物业名称", "field": "物业名称", "flex": 1}, {"headerName": "金额", "field": "金额", "flex": 1}]}}</field>
</record>
</odoo>
+2 -2
View File
@@ -62,12 +62,12 @@
sequence="8"
name="累计欠收"
parent="yuthon_property_income_menu"
action="action_report_placeholder"/>
action="action_cum_arrears_report"/>
<menuitem id="yuthon_property_income_cum_income_menu"
sequence="9"
name="累计收入"
parent="yuthon_property_income_menu"
action="action_report_placeholder"/>
action="action_cum_income_report"/>
<!-- 其他收入 -->
<menuitem id="yuthon_property_other_income_menu"
@@ -8,13 +8,15 @@
{
"sideBar": true,
"report_config": {
"model": "property.accounts.receivable",
"model": "receivable.modify.line",
"method": "get_monthly_precollection_report",
"dateMode": "single",
"columns": [
{"headerName": "序号", "field": "序号", "width": 45, "flex": 0, "pinned": "left"},
{"headerName": "合同编号", "field": "合同编号", "flex": 1},
{"headerName": "物业名称", "field": "物业名称", "flex": 2},
{"headerName": "承租方名称", "field": "承租方名称", "flex": 1.2},
{"headerName": "合同编号", "field": "合同编号", "flex": 1.2},
{"headerName": "物业名称", "field": "物业名称", "flex": 2.5},
{"headerName": "承租方名称", "field": "承租方名称", "flex": 1.4},
{"headerName": "收款类型", "field": "收款类型", "flex": 1},
{"headerName": "金额", "field": "金额", "flex": 1, "type": "number"},
{"headerName": "所属公司", "field": "所属公司", "flex": 1},
{"headerName": "管理员", "field": "管理员", "flex": 1}