Merge branch 'master' into dev_zhou

This commit is contained in:
李鹏宇
2026-01-24 11:36:05 +08:00
60 changed files with 3443 additions and 39 deletions
@@ -78,6 +78,8 @@ class SoongWorkflowRecord(models.Model):
search='_search_is_to_do',
)
priority = fields.Selection([('0', 'Normal'), ('1', 'Urgent')], 'Priority', default='0', index=True)
is_company = fields.Boolean(string="本公司", default=True, compute='_compute_is_company',search='_search_part_of_company')
@api.depends_context('uid', 'company')
@@ -235,6 +235,7 @@
<button name="delete_record" type="object" string="删除" class="oe_highlight"
groups="soong_workflow.group_soong_workflow_manager"/>
</header>
<field name="priority" optional="show" widget="priority" nolabel="1"/>
<field name="code"/>
<field name="workflow_id" string="审批类型"/>
<field name="name"/>
@@ -88,8 +88,7 @@
</group>
</group>
<group>
<field name="scope_chapter_ids" widget="many2many_tags" options="{'no_create': True, 'no_open': True}}"/>
<field name="type_stamp_ids" widget="many2many_tags" domain="[('scope_setting_ids', 'in', scope_chapter_ids)]" options="{'no_create': True, 'no_open': True}}"/>
<field name="type_stamp_ids" widget="many2many_tags" options="{'no_create': True, 'no_open': True}}"/>
</group>
<group string="文件内容">
<field name="main_text" string=" "/>
@@ -96,8 +96,7 @@
<field name="official_documents_ids" widget="preview_many2many"/>
</group>
<group>
<field name="scope_chapter_ids" widget="many2many_tags" options="{'no_create': True, 'no_open': True}}"/>
<field name="type_stamp_ids" widget="many2many_tags" domain="[('scope_setting_ids', 'in', scope_chapter_ids)]" options="{'no_create': True, 'no_open': True}}"/>
<field name="type_stamp_ids" widget="many2many_tags" options="{'no_create': True, 'no_open': True}}"/>
</group>
<group string="文件内容">
<field name="main_text" string=" "/>
@@ -96,8 +96,7 @@
<field name="is_contract" invisible="1"/>
<field name="contract_source_id" invisible="not is_child_contract"/>
<field name="condition" readonly="1" invisible="1"/>
<field name="scope_chapter_ids" widget="many2many_tags" options="{'no_create': True, 'no_open': True}}"/>
<field name="type_stamp_ids" widget="many2many_tags" domain="[('scope_setting_ids', 'in', scope_chapter_ids)]" options="{'no_create': True, 'no_open': True}}"/>
<field name="type_stamp_ids" widget="many2many_tags" options="{'no_create': True, 'no_open': True}}"/>
</group>
</group>
<notebook>
+24
View File
@@ -16,6 +16,30 @@
'views/cumulative_income_report.xml',
'views/overdue_amount_report.xml',
'views/monthly_precollection_report.xml',
# 'views/management_area_report.xml',
# 'views/penalty_detail_report.xml',
# 'views/current_month_received_report.xml',
# 'views/receivable_detail_report.xml',
# 'views/rentable_area_report.xml',
# 'views/cumulative_arrears_report.xml',
# 'views/parking_fee_yearly_report.xml',
'views/current_receivable_rent_report.xml',
# 'views/current_receivable_property_fee_report.xml',
# 'views/parking_fee_current_month_report.xml',
# 'views/parking_fee_current_month_receivable.xml',
# 'views/yearly_penalty_report.xml',
# 'views/deposit_total_report.xml',
# 'views/rented_area_report.xml',
# 'views/yearly_management_fee_report.xml',
# 'views/yearly_expense_report.xml',
# 'views/yearly_assessment_fee_report.xml',
# 'views/yearly_other_fee_report.xml',
# 'views/yearly_maintenance_fee_report.xml',
# 'views/monthly_management_fee_report.xml',
# 'views/monthly_expense_total_report.xml',
# 'views/monthly_assessment_fee_report.xml',
# 'views/monthly_other_expense_report.xml',
# 'views/monthly_maintenance_fee_report.xml',
'views/yuthon_report_res_partner.xml',
'views/att_report_view.xml',
],
+24
View File
@@ -7,4 +7,28 @@ from . import yuthon_contract_report
from . import cumulative_income_report
from . import overdue_amount_report
from . import monthly_precollection_report
# from . import management_area_report
# from . import penalty_detail_report
# from . import current_month_received_report
# from . import receivable_detail_report
# from . import rentable_area_report
# from . import cumulative_arrears_report
# from . import parking_fee_yearly_report
from . import current_receivable_rent_report
# from . import current_receivable_property_fee_report
# from . import parking_fee_current_month_report
# from . import parking_fee_current_month_receivable
# from . import yearly_penalty_report
# from . import deposit_total_report
# from . import rented_area_report
# from . import yearly_management_fee_report
# from . import yearly_expense_report
# from . import yearly_assessment_fee_report
# from . import yearly_other_fee_report
# from . import yearly_maintenance_fee_report
# from . import monthly_management_fee_report
# from . import monthly_expense_total_report
# from . import monthly_assessment_fee_report
# from . import monthly_other_expense_report
# from . import monthly_maintenance_fee_report
from . import att_report
@@ -0,0 +1,88 @@
import datetime
from odoo import api, models
class CumulativeArrearsReport(models.Model):
_inherit = 'property.accounts.receivable'
@api.model
def get_cumulative_arrears_report(self, params):
"""
累计欠收报表:
- 依据应收日期过滤(欠租所属月份)
- 金额:月租金取合同租金(contract_id.rent),欠租金额取 submitted,面积取合同 total_area
"""
domain = []
keyword = params.get("keyword")
if keyword:
domain += [
'|', '|',
('contract_id.contract_code', 'ilike', keyword),
('property_name', 'ilike', keyword),
('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))
if partner_id:
domain.append(('precinct_id', '=', partner_id))
if company_id:
domain.append(('company_id', '=', company_id))
if management_type_id:
domain.append(('management_type_id', '=', management_type_id))
receivables = self.search(domain, limit=3000, order="due_date asc")
rows = []
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
total_arrears += arrears
rows.append({
"序号": idx,
"物业名称": rec.property_name or "",
"面积": 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 "",
"所属公司": 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 "",
})
return {
"rows": rows,
"summary": {
"序号": False,
"物业名称": False,
"面积": False,
"承租方名称": False,
"合同编号": False,
"月租金": False,
"欠租金额": round(total_arrears, 2),
"欠租所属月份": False,
"所属公司": False,
"管理员": False,
}
}
@@ -0,0 +1,92 @@
import datetime
from odoo import api, models
class CurrentMonthReceivedReport(models.Model):
_inherit = 'property.accounts.receivable'
@api.model
def get_current_month_received_report(self, params):
"""归属本月实收报表:按收款日期筛选已收金额。"""
domain = []
keyword = params.get("keyword")
if keyword:
domain += [
'|', '|',
('contract_id.contract_code', 'ilike', keyword),
('property_name', 'ilike', keyword),
('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:
# 优先按收款日期过滤;若收款日期为空,则按应收日期兜底
domain += [
'|',
'&', ('payout_date', '>=', start_dt), ('payout_date', '<=', end_dt),
'&', ('payout_date', '=', False), '&', ('due_date', '>=', start_dt), ('due_date', '<=', end_dt)
]
elif start_dt:
domain += [
'|',
('payout_date', '>=', start_dt),
'&', ('payout_date', '=', False), ('due_date', '>=', start_dt)
]
elif end_dt:
domain += [
'|',
('payout_date', '<=', end_dt),
'&', ('payout_date', '=', False), ('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))
if partner_id:
domain.append(('precinct_id', '=', partner_id))
if company_id:
domain.append(('company_id', '=', company_id))
if management_type_id:
domain.append(('management_type_id', '=', management_type_id))
receivables = self.search(domain, order="payout_date asc, due_date asc")
rows = []
total_amount = 0.0
for idx, rec in enumerate(receivables, start=1):
amount = rec.delivered1 or 0.0 # 已收金额
total_amount += amount
rows.append({
"序号": idx,
"合同编号": rec.contract_id.contract_code if rec.contract_id else "",
"物业名称": rec.property_name or "",
"承租方名称": rec.lessee_id.name if rec.lessee_id else "",
"金额": round(amount, 2),
"所属公司": rec.company_id.referred if rec.company_id and hasattr(rec.company_id, 'referred') else rec.company_id.name if rec.company_id else "",
"管理员": rec.admin_id.name if rec.admin_id else "",
})
return {
"rows": rows,
"summary": {
"序号": False,
"合同编号": "合计",
"物业名称": False,
"承租方名称": False,
"金额": round(total_amount, 2),
"所属公司": False,
"管理员": False,
}
}
@@ -0,0 +1,112 @@
import datetime
from odoo import api, models
class CurrentReceivablePropertyFeeReport(models.Model):
_inherit = 'property.accounts.receivable'
@api.model
def get_current_receivable_property_fee_report(self, params):
"""
当前应收-物管水电费:
- 按应收日期过滤
- 默认管理类型名称包含“管”或“水”或“电”,可通过 management_type_id 精确指定
- 含税金额:original_amount
- 不含税金额:original_amount / (1 + tax/100)
"""
domain = []
keyword = params.get("keyword")
if keyword:
domain += [
'|', '|',
('contract_id.contract_code', 'ilike', keyword),
('property_name', 'ilike', keyword),
('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))
if partner_id:
domain.append(('precinct_id', '=', partner_id))
if company_id:
domain.append(('company_id', '=', company_id))
if management_type_id:
domain.append(('management_type_id', '=', management_type_id))
type_domain = domain[:]
else:
# 更宽泛:物业/物管/管/水/电/能源等
types = self.env['yuthon.management.type'].search([
'|', '|', '|', '|',
('name', 'ilike', '物'),
('name', 'ilike', '管'),
('name', 'ilike', '物业'),
('name', 'ilike', '水'),
('name', 'ilike', '电'),
])
if types:
type_domain = domain + [('management_type_id', 'in', types.ids)]
else:
type_domain = domain[:]
# 先按类型+日期,再放宽日期,再放宽类型
receivables = self.search(type_domain, limit=3000, order="due_date asc")
if not receivables and type_domain != domain:
receivables = self.search(domain, limit=3000, order="due_date asc")
rows = []
total_amount = 0.0
total_amount_nt = 0.0
for idx, rec in enumerate(receivables, start=1):
amt = rec.original_amount or 0.0
tax_rate = rec.tax or 0.0
amt_nt = amt / (1 + tax_rate / 100) if tax_rate else amt
total_amount += amt
total_amount_nt += amt_nt
rows.append({
"序号": idx,
"应收租期": rec.due_date.strftime("%Y-%m-%d") if rec.due_date else "",
"承租方名称": rec.lessee_id.name if rec.lessee_id else "",
"金额(含税)": round(amt, 2),
"税率": tax_rate,
"金额(不含税)": round(amt_nt, 2),
"物业名称": rec.property_name or "",
"产权证": rec.title_deed or "",
"合同编号": rec.contract_id.contract_code if rec.contract_id else "",
"管理员": rec.admin_id.name if rec.admin_id else "",
})
return {
"rows": rows,
"summary": {
"序号": False,
"应收租期": False,
"承租方名称": "合计:",
"金额(含税)": round(total_amount, 2),
"税率": False,
"金额(不含税)": round(total_amount_nt, 2),
"物业名称": False,
"产权证": False,
"合同编号": False,
"管理员": False,
}
}
@@ -0,0 +1,109 @@
import datetime
from odoo import api, models
class CurrentReceivableRentReport(models.Model):
_inherit = 'property.accounts.receivable'
@api.model
def get_current_receivable_rent_report(self, params):
"""
当期应收-租金:
- 过滤应收日期区间
- 默认管理类型包含“租”,可通过 management_type_id 精确指定
- 含税金额:original_amount
- 不含税金额:original_amount / (1 + tax/100)
"""
domain = []
keyword = params.get("keyword")
if keyword:
domain += [
'|', '|',
('contract_id.contract_code', 'ilike', keyword),
('property_name', 'ilike', keyword),
('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))
if partner_id:
domain.append(('precinct_id', '=', partner_id))
if company_id:
domain.append(('company_id', '=', company_id))
if management_type_id:
domain.append(('management_type_id', '=', management_type_id))
else:
rent_types = self.env['yuthon.management.type'].search([('name', 'ilike', '租')])
if rent_types:
domain.append(('management_type_id', 'in', rent_types.ids))
receivables = self.search(domain, limit=3000, order="due_date asc")
rows = []
total_amount = 0.0
total_amount_nt = 0.0
for idx, rec in enumerate(receivables, start=1):
amt = rec.original_amount or 0.0
# 从property.rent.list中获取税率
rent_list = self.env['property.rent.list'].search([
('lessee_id', '=', rec.lessee_id.id if rec.lessee_id else False),
('property_id', 'in', rec.property_ids.ids if rec.property_ids else []),
('due_date', '=', rec.due_date),
], limit=1)
tax_rate = rent_list.tax if rent_list else (rec.tax or 0.0)
amt_nt = amt / (1 + tax_rate / 100) if tax_rate else amt
total_amount += amt
total_amount_nt += amt_nt
rows.append({
"序号": idx,
"应收开始": rec.due_date.strftime("%Y-%m-%d") if rec.due_date else "",
"应收结束": rec.due_date_end.strftime("%Y-%m-%d") if rec.due_date_end else "",
"承租方名称": rec.lessee_id.name if rec.lessee_id else "",
"金额(含税)": round(amt, 2),
"税率": tax_rate,
"金额(不含税)": round(amt_nt, 2),
"物业名称": rec.property_name or "",
"产权证": rec.title_deed or "",
"合同编号": rec.contract_id.contract_code if rec.contract_id else "",
"管理员": rec.admin_id.name if rec.admin_id else "",
"acc_type": rec.acc_type if rec.acc_type else False,
})
# 返回字典格式,包含数据行和合计行
return {
"rows": rows,
"summary": {
"序号": "",
"应收开始": False,
"应收结束": False,
"承租方名称": "合计:",
"金额(含税)": round(total_amount, 2),
"税率": "",
"金额(不含税)": round(total_amount_nt, 2),
"物业名称": False,
"产权证": False,
"合同编号": False,
"管理员": False,
}
}
@@ -0,0 +1,80 @@
from odoo import api, models
class DepositTotalReport(models.Model):
_inherit = 'property.accounts.receivable'
@api.model
def get_deposit_total_report(self, params):
"""
押金累计总额:
- 默认查询管理类型含“押金”的应收账单
- 支持关键词搜索合同/物业/承租方
- 金额取 original_amount
"""
domain = []
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 = 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))
if partner_id:
domain.append(('precinct_id', '=', partner_id))
if company_id:
domain.append(('company_id', '=', company_id))
# 管理类型
if management_type_id:
type_domain = domain + [('management_type_id', '=', management_type_id)]
else:
deposit_types = self.env['yuthon.management.type'].search([('name', 'ilike', '押金')])
if deposit_types:
type_domain = domain + [('management_type_id', 'in', deposit_types.ids)]
else:
type_domain = domain[:]
receivables = self.search(type_domain, limit=3000, order="due_date asc")
if not receivables and type_domain != domain:
receivables = self.search(domain, limit=3000, order="due_date asc")
rows = []
total_amount = 0.0
for idx, rec in enumerate(receivables, start=1):
amount = rec.original_amount or 0.0
total_amount += amount
rows.append({
"序号": idx,
"合同编号": rec.contract_id.contract_code if rec.contract_id else "",
"物业名称": rec.property_name or "",
"承租方名称": rec.lessee_id.name if rec.lessee_id else "",
"金额": round(amount, 2),
"所属公司": rec.company_id.referred if rec.company_id and hasattr(rec.company_id, 'referred') else rec.company_id.name if rec.company_id else "",
"管理员": rec.admin_id.name if rec.admin_id else "",
})
return {
"rows": rows,
"summary": {
"序号": False,
"合同编号": False,
"物业名称": False,
"承租方名称": "合计:",
"金额": round(total_amount, 2),
"所属公司": False,
"管理员": False,
}
}
@@ -0,0 +1,88 @@
import datetime
from odoo import api, models
class ManagementAreaReport(models.Model):
_inherit = 'yuthon.property'
@api.model
def get_management_area_report(self, params):
"""管理面积报表数据源。"""
domain = []
keyword = params.get("keyword")
if keyword:
domain += [
'|', '|',
('name', 'ilike', keyword),
('title_deed', 'ilike', keyword),
('code', 'ilike', keyword),
]
date_start = params.get("date_start")
date_end = params.get("date_end")
end_dt = datetime.datetime.fromisoformat(date_end).date() if date_end else None
condition = params.get('condition')
partner_id = params.get('partner_id')
company_id = params.get('company_id')
if condition:
domain.append(('tenure_type', '=', condition))
if partner_id:
domain.append(('precinct_id', '=', partner_id))
if company_id:
domain.append(('company_id', '=', company_id))
properties = self.search(domain, limit=2000)
rows = []
total_rent_area = 0.0
total_equity_area = 0.0
for idx, prop in enumerate(properties, start=1):
# 按结束日期或当前日期选取有效管理信息
target_date = end_dt or datetime.date.today()
valid_info = prop.manage_information_ids.filtered(
lambda info: info.start_time <= target_date and (not info.end_date or info.end_date >= target_date)
)[:1]
# 若指定了日期范围但无有效管理信息,则跳过
if (date_start or date_end) and not valid_info:
continue
management_area = valid_info.management_area if valid_info else prop.management_area
rent_area = valid_info.rent_area if valid_info else prop.rent_area
company = valid_info.company_id if valid_info else prop.company_id
purpose = valid_info.purpose_id if valid_info else prop.purpose_id
message_standard = prop.message_standard_id # 信息行无管理费标准字段,直接取物业字段
total_rent_area += rent_area or 0.0
total_equity_area += prop.equity_area or 0.0
rows.append({
"序号": idx,
"物业id": prop.id,
"产权证号": prop.title_deed or "",
"物业名称": prop.name or "",
"可租面积": round(rent_area or 0.0, 4),
"产权面积": round(prop.equity_area or 0.0, 4),
"用途": purpose.name if purpose else "",
"管理费标准": message_standard.name if message_standard else "",
})
return {
"rows": rows,
"summary": {
"序号": False,
"物业id": "合计",
"产权证号": False,
"物业名称": False,
"可租面积": round(total_rent_area, 4),
"产权面积": round(total_equity_area, 4),
"用途": False,
"管理费标准": False,
}
}
@@ -0,0 +1,111 @@
import datetime
from odoo import api, models
class MonthlyAssessmentFeeReport(models.Model):
_inherit = 'property.accounts.receivable'
@api.model
def get_monthly_assessment_fee_report(self, params):
"""
本月支出-评估费:
- 默认当月,支持 date_start/date_end 覆盖
- 管理类型默认匹配“评估/估”,可传 management_type_id 精确指定
- 金额取 original_amount
- 日期优先收款日期 payout_date,若无则应收日期 due_date
- 备注取 remark
"""
domain = []
keyword = params.get("keyword")
if keyword:
domain += [
'|', '|',
('contract_id.contract_code', 'ilike', keyword),
('property_name', 'ilike', keyword),
('lessee_id.name', 'ilike', keyword),
]
# 当月时间范围
today = datetime.date.today()
month_start = today.replace(day=1)
if month_start.month == 12:
month_end = month_start.replace(year=month_start.year + 1, month=1) - datetime.timedelta(days=1)
else:
month_end = month_start.replace(month=month_start.month + 1) - datetime.timedelta(days=1)
date_start = params.get("date_start")
date_end = params.get("date_end")
start_dt = datetime.datetime.fromisoformat(date_start).date() if date_start else month_start
end_dt = datetime.datetime.fromisoformat(date_end).date() if date_end else month_end
date_domain = [
'|',
'&', ('payout_date', '>=', start_dt), ('payout_date', '<=', end_dt),
'&', ('payout_date', '=', False), ('due_date', '>=', start_dt), ('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')
base_domain = []
if condition:
base_domain.append(('tenure_type', '=', condition))
if partner_id:
base_domain.append(('precinct_id', '=', partner_id))
if company_id:
base_domain.append(('company_id', '=', company_id))
if management_type_id:
type_domain = base_domain + [('management_type_id', '=', management_type_id)]
else:
assess_types = self.env['yuthon.management.type'].search([
'|',
('name', 'ilike', '评估'),
('name', 'ilike', '估'),
])
if assess_types:
type_domain = base_domain + [('management_type_id', 'in', assess_types.ids)]
else:
type_domain = base_domain[:]
receivables = self.search(type_domain + date_domain, limit=2000, order="payout_date asc, due_date asc")
if not receivables and type_domain != base_domain:
receivables = self.search(base_domain + date_domain, limit=2000, order="payout_date asc, due_date asc")
if not receivables and type_domain != base_domain:
receivables = self.search(base_domain, limit=2000, order="payout_date asc, due_date asc")
rows = []
total_amount = 0.0
for idx, rec in enumerate(receivables, start=1):
amount = rec.original_amount or 0.0
total_amount += amount
pay_date = rec.payout_date or rec.due_date
rows.append({
"序号": idx,
"编码": rec.contract_id.contract_code if rec.contract_id else rec.code if hasattr(rec, 'code') else "",
"日期": pay_date.strftime("%Y-%m-%d") if pay_date else "",
"物业名称": rec.property_name or "",
"金额": round(amount, 2),
"备注": rec.remark or "",
"所属公司": rec.company_id.referred if rec.company_id and hasattr(rec.company_id, 'referred') else rec.company_id.name if rec.company_id else "",
"管理员": rec.admin_id.name if rec.admin_id else "",
})
return {
"rows": rows,
"summary": {
"序号": False,
"编码": False,
"日期": False,
"物业名称": False,
"金额": round(total_amount, 2),
"备注": False,
"所属公司": False,
"管理员": False,
}
}
@@ -0,0 +1,98 @@
import datetime
from odoo import api, models
class MonthlyExpenseTotalReport(models.Model):
_inherit = 'property.accounts.receivable'
@api.model
def get_monthly_expense_total_report(self, params):
"""
本月支出-合计:
- 默认统计当前月份,可用 date_start/date_end 覆盖
- 不限定管理类型(可在前端传 management_type_id 精确过滤)
- 金额取 original_amount
- 日期优先收款日期 payout_date,若为空则应收日期 due_date
- 备注取 remark
"""
domain = []
keyword = params.get("keyword")
if keyword:
domain += [
'|', '|',
('contract_id.contract_code', 'ilike', keyword),
('property_name', 'ilike', keyword),
('lessee_id.name', 'ilike', keyword),
]
# 当月范围
today = datetime.date.today()
month_start = today.replace(day=1)
if month_start.month == 12:
month_end = month_start.replace(year=month_start.year + 1, month=1) - datetime.timedelta(days=1)
else:
month_end = month_start.replace(month=month_start.month + 1) - datetime.timedelta(days=1)
date_start = params.get("date_start")
date_end = params.get("date_end")
start_dt = datetime.datetime.fromisoformat(date_start).date() if date_start else month_start
end_dt = datetime.datetime.fromisoformat(date_end).date() if date_end else month_end
date_domain = [
'|',
'&', ('payout_date', '>=', start_dt), ('payout_date', '<=', end_dt),
'&', ('payout_date', '=', False), ('due_date', '>=', start_dt), ('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')
base_domain = []
if condition:
base_domain.append(('tenure_type', '=', condition))
if partner_id:
base_domain.append(('precinct_id', '=', partner_id))
if company_id:
base_domain.append(('company_id', '=', company_id))
if management_type_id:
base_domain.append(('management_type_id', '=', management_type_id))
receivables = self.search(base_domain + date_domain, limit=3000, order="payout_date asc, due_date asc")
if not receivables:
receivables = self.search(base_domain, limit=3000, order="payout_date asc, due_date asc")
rows = []
total_amount = 0.0
for idx, rec in enumerate(receivables, start=1):
amount = rec.original_amount or 0.0
total_amount += amount
pay_date = rec.payout_date or rec.due_date
rows.append({
"序号": idx,
"编码": rec.contract_id.contract_code if rec.contract_id else rec.code if hasattr(rec, 'code') else "",
"日期": pay_date.strftime("%Y-%m-%d") if pay_date else "",
"物业名称": rec.property_name or "",
"金额": round(amount, 2),
"备注": rec.remark or "",
"所属公司": rec.company_id.referred if rec.company_id and hasattr(rec.company_id, 'referred') else rec.company_id.name if rec.company_id else "",
"管理员": rec.admin_id.name if rec.admin_id else "",
})
return {
"rows": rows,
"summary": {
"序号": False,
"编码": False,
"日期": False,
"物业名称": False,
"金额": round(total_amount, 2),
"备注": False,
"所属公司": False,
"管理员": False,
}
}
@@ -0,0 +1,111 @@
import datetime
from odoo import api, models
class MonthlyMaintenanceFeeReport(models.Model):
_inherit = 'property.accounts.receivable'
@api.model
def get_monthly_maintenance_fee_report(self, params):
"""
本月支出-维修费:
- 默认当月,可用 date_start/date_end 覆盖
- 管理类型默认匹配“维/维修”,可传 management_type_id 精确指定
- 金额取 original_amount
- 日期优先收款日期 payout_date,若无则应收日期 due_date
- 备注取 remark
"""
domain = []
keyword = params.get("keyword")
if keyword:
domain += [
'|', '|',
('contract_id.contract_code', 'ilike', keyword),
('property_name', 'ilike', keyword),
('lessee_id.name', 'ilike', keyword),
]
# 当月时间范围
today = datetime.date.today()
month_start = today.replace(day=1)
if month_start.month == 12:
month_end = month_start.replace(year=month_start.year + 1, month=1) - datetime.timedelta(days=1)
else:
month_end = month_start.replace(month=month_start.month + 1) - datetime.timedelta(days=1)
date_start = params.get("date_start")
date_end = params.get("date_end")
start_dt = datetime.datetime.fromisoformat(date_start).date() if date_start else month_start
end_dt = datetime.datetime.fromisoformat(date_end).date() if date_end else month_end
date_domain = [
'|',
'&', ('payout_date', '>=', start_dt), ('payout_date', '<=', end_dt),
'&', ('payout_date', '=', False), ('due_date', '>=', start_dt), ('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')
base_domain = []
if condition:
base_domain.append(('tenure_type', '=', condition))
if partner_id:
base_domain.append(('precinct_id', '=', partner_id))
if company_id:
base_domain.append(('company_id', '=', company_id))
if management_type_id:
type_domain = base_domain + [('management_type_id', '=', management_type_id)]
else:
maintenance_types = self.env['yuthon.management.type'].search([
'|',
('name', 'ilike', '维'),
('name', 'ilike', '维修'),
])
if maintenance_types:
type_domain = base_domain + [('management_type_id', 'in', maintenance_types.ids)]
else:
type_domain = base_domain[:]
receivables = self.search(type_domain + date_domain, limit=3000, order="payout_date asc, due_date asc")
if not receivables and type_domain != base_domain:
receivables = self.search(base_domain + date_domain, limit=3000, order="payout_date asc, due_date asc")
if not receivables and type_domain != base_domain:
receivables = self.search(base_domain, limit=3000, order="payout_date asc, due_date asc")
rows = []
total_amount = 0.0
for idx, rec in enumerate(receivables, start=1):
amount = rec.original_amount or 0.0
total_amount += amount
pay_date = rec.payout_date or rec.due_date
rows.append({
"序号": idx,
"编码": rec.contract_id.contract_code if rec.contract_id else rec.code if hasattr(rec, 'code') else "",
"日期": pay_date.strftime("%Y-%m-%d") if pay_date else "",
"物业名称": rec.property_name or "",
"金额": round(amount, 2),
"备注": rec.remark or "",
"所属公司": rec.company_id.referred if rec.company_id and hasattr(rec.company_id, 'referred') else rec.company_id.name if rec.company_id else "",
"管理员": rec.admin_id.name if rec.admin_id else "",
})
return {
"rows": rows,
"summary": {
"序号": False,
"编码": False,
"日期": False,
"物业名称": False,
"金额": round(total_amount, 2),
"备注": False,
"所属公司": False,
"管理员": False,
}
}
@@ -0,0 +1,112 @@
import datetime
from odoo import api, models
class MonthlyManagementFeeReport(models.Model):
_inherit = 'property.accounts.receivable'
@api.model
def get_monthly_management_fee_report(self, params):
"""
本月支出-管理费:
- 默认统计当月,支持 date_start/date_end 覆盖
- 管理类型默认匹配名称含“管/管理/物管”,可传 management_type_id 精确指定
- 金额取 original_amount
- 日期优先收款日期 payout_date,若无则应收日期 due_date
- 备注取 remark
"""
domain = []
keyword = params.get("keyword")
if keyword:
domain += [
'|', '|',
('contract_id.contract_code', 'ilike', keyword),
('property_name', 'ilike', keyword),
('lessee_id.name', 'ilike', keyword),
]
# 当月时间范围
today = datetime.date.today()
month_start = today.replace(day=1)
if month_start.month == 12:
month_end = month_start.replace(year=month_start.year + 1, month=1) - datetime.timedelta(days=1)
else:
month_end = month_start.replace(month=month_start.month + 1) - datetime.timedelta(days=1)
date_start = params.get("date_start")
date_end = params.get("date_end")
start_dt = datetime.datetime.fromisoformat(date_start).date() if date_start else month_start
end_dt = datetime.datetime.fromisoformat(date_end).date() if date_end else month_end
date_domain = [
'|',
'&', ('payout_date', '>=', start_dt), ('payout_date', '<=', end_dt),
'&', ('payout_date', '=', False), ('due_date', '>=', start_dt), ('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')
base_domain = []
if condition:
base_domain.append(('tenure_type', '=', condition))
if partner_id:
base_domain.append(('precinct_id', '=', partner_id))
if company_id:
base_domain.append(('company_id', '=', company_id))
if management_type_id:
type_domain = base_domain + [('management_type_id', '=', management_type_id)]
else:
mgmt_types = self.env['yuthon.management.type'].search([
'|', '|',
('name', 'ilike', '管'),
('name', 'ilike', '管理'),
('name', 'ilike', '物管'),
])
if mgmt_types:
type_domain = base_domain + [('management_type_id', 'in', mgmt_types.ids)]
else:
type_domain = base_domain[:]
receivables = self.search(type_domain + date_domain, limit=2000, order="payout_date asc, due_date asc")
if not receivables and type_domain != base_domain:
receivables = self.search(base_domain + date_domain, limit=2000, order="payout_date asc, due_date asc")
if not receivables and type_domain != base_domain:
receivables = self.search(base_domain, limit=2000, order="payout_date asc, due_date asc")
rows = []
total_amount = 0.0
for idx, rec in enumerate(receivables, start=1):
amount = rec.original_amount or 0.0
total_amount += amount
pay_date = rec.payout_date or rec.due_date
rows.append({
"序号": idx,
"编码": rec.contract_id.contract_code if rec.contract_id else rec.code if hasattr(rec, 'code') else "",
"日期": pay_date.strftime("%Y-%m-%d") if pay_date else "",
"物业名称": rec.property_name or "",
"金额": round(amount, 2),
"备注": rec.remark or "",
"所属公司": rec.company_id.referred if rec.company_id and hasattr(rec.company_id, 'referred') else rec.company_id.name if rec.company_id else "",
"管理员": rec.admin_id.name if rec.admin_id else "",
})
return {
"rows": rows,
"summary": {
"序号": False,
"编码": False,
"日期": False,
"物业名称": False,
"金额": round(total_amount, 2),
"备注": False,
"所属公司": False,
"管理员": False,
}
}
@@ -0,0 +1,111 @@
import datetime
from odoo import api, models
class MonthlyOtherExpenseReport(models.Model):
_inherit = 'property.accounts.receivable'
@api.model
def get_monthly_other_expense_report(self, params):
"""
本月支出-其他:
- 默认当月,支持 date_start/date_end 覆盖
- 管理类型默认匹配“其他/其它”,可传 management_type_id 精确指定
- 金额取 original_amount
- 日期优先收款日期 payout_date,若无则应收日期 due_date
- 备注取 remark
"""
domain = []
keyword = params.get("keyword")
if keyword:
domain += [
'|', '|',
('contract_id.contract_code', 'ilike', keyword),
('property_name', 'ilike', keyword),
('lessee_id.name', 'ilike', keyword),
]
# 当月时间范围
today = datetime.date.today()
month_start = today.replace(day=1)
if month_start.month == 12:
month_end = month_start.replace(year=month_start.year + 1, month=1) - datetime.timedelta(days=1)
else:
month_end = month_start.replace(month=month_start.month + 1) - datetime.timedelta(days=1)
date_start = params.get("date_start")
date_end = params.get("date_end")
start_dt = datetime.datetime.fromisoformat(date_start).date() if date_start else month_start
end_dt = datetime.datetime.fromisoformat(date_end).date() if date_end else month_end
date_domain = [
'|',
'&', ('payout_date', '>=', start_dt), ('payout_date', '<=', end_dt),
'&', ('payout_date', '=', False), ('due_date', '>=', start_dt), ('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')
base_domain = []
if condition:
base_domain.append(('tenure_type', '=', condition))
if partner_id:
base_domain.append(('precinct_id', '=', partner_id))
if company_id:
base_domain.append(('company_id', '=', company_id))
if management_type_id:
type_domain = base_domain + [('management_type_id', '=', management_type_id)]
else:
other_types = self.env['yuthon.management.type'].search([
'|',
('name', 'ilike', '其他'),
('name', 'ilike', '其它'),
])
if other_types:
type_domain = base_domain + [('management_type_id', 'in', other_types.ids)]
else:
type_domain = base_domain[:]
receivables = self.search(type_domain + date_domain, limit=3000, order="payout_date asc, due_date asc")
if not receivables and type_domain != base_domain:
receivables = self.search(base_domain + date_domain, limit=3000, order="payout_date asc, due_date asc")
if not receivables and type_domain != base_domain:
receivables = self.search(base_domain, limit=3000, order="payout_date asc, due_date asc")
rows = []
total_amount = 0.0
for idx, rec in enumerate(receivables, start=1):
amount = rec.original_amount or 0.0
total_amount += amount
pay_date = rec.payout_date or rec.due_date
rows.append({
"序号": idx,
"编码": rec.contract_id.contract_code if rec.contract_id else rec.code if hasattr(rec, 'code') else "",
"日期": pay_date.strftime("%Y-%m-%d") if pay_date else "",
"物业名称": rec.property_name or "",
"金额": round(amount, 2),
"备注": rec.remark or "",
"所属公司": rec.company_id.referred if rec.company_id and hasattr(rec.company_id, 'referred') else rec.company_id.name if rec.company_id else "",
"管理员": rec.admin_id.name if rec.admin_id else "",
})
return {
"rows": rows,
"summary": {
"序号": False,
"编码": False,
"日期": False,
"物业名称": False,
"金额": round(total_amount, 2),
"备注": False,
"所属公司": False,
"管理员": False,
}
}
@@ -0,0 +1,99 @@
import datetime
from odoo import api, models
class ParkingFeeCurrentMonthReceivable(models.Model):
_inherit = 'property.accounts.receivable'
@api.model
def get_parking_fee_current_month_receivable(self, params):
"""
停车费本月应收:
- 默认过滤当月应收日期;可用 date_start/date_end 覆盖
- 默认管理类型名称含“车”,也可传 management_type_id 精确指定
- 金额取应收 original_amount
"""
domain = []
# 关键词
keyword = params.get("keyword")
if keyword:
domain += [
'|', '|',
('contract_id.contract_code', 'ilike', keyword),
('property_name', 'ilike', keyword),
('lessee_id.name', 'ilike', keyword),
]
# 时间范围(默认当月)
today = datetime.date.today()
month_start = today.replace(day=1)
if month_start.month == 12:
month_end = month_start.replace(year=month_start.year + 1, month=1) - datetime.timedelta(days=1)
else:
month_end = month_start.replace(month=month_start.month + 1) - datetime.timedelta(days=1)
date_start = params.get("date_start")
date_end = params.get("date_end")
start_dt = datetime.datetime.fromisoformat(date_start).date() if date_start else month_start
end_dt = datetime.datetime.fromisoformat(date_end).date() if date_end else month_end
domain.append(("due_date", ">=", start_dt))
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))
if partner_id:
domain.append(('precinct_id', '=', partner_id))
if company_id:
domain.append(('company_id', '=', company_id))
# 管理类型
if management_type_id:
type_domain = domain + [('management_type_id', '=', management_type_id)]
else:
parking_types = self.env['yuthon.management.type'].search([('name', 'ilike', '车')])
if parking_types:
type_domain = domain + [('management_type_id', 'in', parking_types.ids)]
else:
type_domain = domain[:]
receivables = self.search(type_domain, limit=2000, order="due_date asc")
if not receivables and type_domain != domain:
receivables = self.search(domain, limit=2000, order="due_date asc")
rows = []
total_amount = 0.0
for idx, rec in enumerate(receivables, start=1):
amount = rec.original_amount or 0.0
total_amount += amount
rows.append({
"序号": idx,
"合同编号": rec.contract_id.contract_code if rec.contract_id else "",
"物业名称": rec.property_name or "",
"承租方名称": rec.lessee_id.name if rec.lessee_id else "",
"金额": round(amount, 2),
"所属公司": rec.company_id.referred if rec.company_id and hasattr(rec.company_id, 'referred') else rec.company_id.name if rec.company_id else "",
"管理员": rec.admin_id.name if rec.admin_id else "",
})
return {
"rows": rows,
"summary": {
"序号": False,
"合同编号": False,
"物业名称": False,
"承租方名称": "合计:",
"金额": round(total_amount, 2),
"所属公司": False,
"管理员": False,
}
}
@@ -0,0 +1,105 @@
import datetime
from odoo import api, models
class ParkingFeeCurrentMonthReport(models.Model):
_inherit = 'property.accounts.receivable'
@api.model
def get_parking_fee_current_month_report(self, params):
"""
停车费本月实收:
- 默认过滤当月收款日期,收款日期为空则用应收日期兜底
- 默认管理类型名称含“车”,可通过 management_type_id 精确指定
- 金额取已收 delivered1
"""
domain = []
keyword = params.get("keyword")
if keyword:
domain += [
'|', '|',
('contract_id.contract_code', 'ilike', keyword),
('property_name', 'ilike', keyword),
('lessee_id.name', 'ilike', keyword),
]
# 当月时间范围
today = datetime.date.today()
month_start = today.replace(day=1)
if month_start.month == 12:
month_end = month_start.replace(year=month_start.year + 1, month=1) - datetime.timedelta(days=1)
else:
month_end = month_start.replace(month=month_start.month + 1) - datetime.timedelta(days=1)
date_start = params.get("date_start")
date_end = params.get("date_end")
start_dt = datetime.datetime.fromisoformat(date_start).date() if date_start else month_start
end_dt = datetime.datetime.fromisoformat(date_end).date() if date_end else month_end
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))
if partner_id:
domain.append(('precinct_id', '=', partner_id))
if company_id:
domain.append(('company_id', '=', company_id))
# 管理类型
if management_type_id:
type_domain = domain + [('management_type_id', '=', management_type_id)]
else:
parking_types = self.env['yuthon.management.type'].search([('name', 'ilike', '车')])
if parking_types:
type_domain = domain + [('management_type_id', 'in', parking_types.ids)]
else:
type_domain = domain[:]
# 日期过滤:收款日期优先,收款为空则应收日期
date_domain = [
'|',
'&', ('payout_date', '>=', start_dt), ('payout_date', '<=', end_dt),
'&', ('payout_date', '=', False), ('due_date', '>=', start_dt), ('due_date', '<=', end_dt),
]
receivables = self.search(type_domain + date_domain, limit=2000, order="payout_date asc, due_date asc")
if not receivables:
receivables = self.search(type_domain, limit=2000, order="payout_date asc, due_date asc")
if not receivables and type_domain != domain:
receivables = self.search(domain + date_domain, limit=2000, order="payout_date asc, due_date asc")
if not receivables and type_domain != domain:
receivables = self.search(domain, limit=2000, order="payout_date asc, due_date asc")
rows = []
total_amount = 0.0
for idx, rec in enumerate(receivables, start=1):
amount = rec.delivered1 or 0.0
total_amount += amount
rows.append({
"序号": idx,
"合同编号": rec.contract_id.contract_code if rec.contract_id else "",
"物业名称": rec.property_name or "",
"承租方名称": rec.lessee_id.name if rec.lessee_id else "",
"金额": round(amount, 2),
"所属公司": rec.company_id.referred if rec.company_id and hasattr(rec.company_id, 'referred') else rec.company_id.name if rec.company_id else "",
"管理员": rec.admin_id.name if rec.admin_id else "",
})
return {
"rows": rows,
"summary": {
"序号": False,
"合同编号": False,
"物业名称": False,
"承租方名称": "合计:",
"金额": round(total_amount, 2),
"所属公司": False,
"管理员": False,
}
}
@@ -0,0 +1,110 @@
import datetime
from odoo import api, models
class ParkingFeeYearlyReport(models.Model):
_inherit = 'property.accounts.receivable'
@api.model
def get_parking_fee_yearly_report(self, params):
"""
停车费本年累计:
- 默认取当年1月1日至12月31日的停车费账单
- 允许传入 date_start/date_end 覆盖
- 金额取 original_amount
"""
domain = []
# 关键词
keyword = params.get("keyword")
if keyword:
domain += [
'|', '|',
('contract_id.contract_code', 'ilike', keyword),
('property_name', 'ilike', keyword),
('lessee_id.name', 'ilike', keyword),
]
# 时间范围(默认当年)
today = datetime.date.today()
year_start = datetime.date(today.year, 1, 1)
year_end = datetime.date(today.year, 12, 31)
date_start = params.get("date_start")
date_end = params.get("date_end")
start_dt = datetime.datetime.fromisoformat(date_start).date() if date_start else year_start
end_dt = datetime.datetime.fromisoformat(date_end).date() if date_end else year_end
domain.append(("due_date", ">=", start_dt))
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))
if partner_id:
domain.append(('precinct_id', '=', partner_id))
if company_id:
domain.append(('company_id', '=', company_id))
if management_type_id:
domain.append(('management_type_id', '=', management_type_id))
parking_domain = domain[:]
else:
# 默认匹配管理类型名称含“车”(停车/车位等),若无匹配则不过滤
parking_types = self.env['yuthon.management.type'].search([('name', 'ilike', '车')])
if parking_types:
parking_domain = domain + [('management_type_id', 'in', parking_types.ids)]
else:
parking_domain = domain[:]
date_domain = [
'|',
'&', ('payout_date', '>=', start_dt), ('payout_date', '<=', end_dt),
'&', ('payout_date', '=', False), ('due_date', '>=', start_dt), ('due_date', '<=', end_dt),
]
# 1) 按停车管理类型 + 日期
receivables = self.search(parking_domain + date_domain, limit=3000, order="payout_date asc, due_date asc")
# 2) 无结果时,移除日期过滤
if not receivables:
receivables = self.search(parking_domain, limit=3000, order="payout_date asc, due_date asc")
# 3) 仍无结果,移除停车管理类型过滤(仅保留日期)
if not receivables and parking_domain != domain:
receivables = self.search(domain + date_domain, limit=3000, order="payout_date asc, due_date asc")
# 4) 仍无结果,再移除日期,彻底兜底
if not receivables and parking_domain != domain:
receivables = self.search(domain, limit=3000, order="payout_date asc, due_date asc")
rows = []
total_amount = 0.0
for idx, rec in enumerate(receivables, start=1):
amount = rec.original_amount or 0.0
total_amount += amount
rows.append({
"序号": idx,
"合同编号": rec.contract_id.contract_code if rec.contract_id else "",
"物业名称": rec.property_name or "",
"承租方名称": rec.lessee_id.name if rec.lessee_id else "",
"金额": round(amount, 2),
"所属公司": rec.company_id.referred if rec.company_id and hasattr(rec.company_id, 'referred') else rec.company_id.name if rec.company_id else "",
"管理员": rec.admin_id.name if rec.admin_id else "",
})
return {
"rows": rows,
"summary": {
"序号": False,
"合同编号": False,
"物业名称": False,
"承租方名称": "合计:",
"金额": round(total_amount, 2),
"所属公司": False,
"管理员": False,
}
}
@@ -0,0 +1,129 @@
import datetime
from odoo import api, models
class PenaltyDetailReport(models.Model):
_inherit = 'property.accounts.receivable'
@api.model
def get_penalty_detail_report(self, params):
"""违约金明细报表(仅取管理类型包含“违约”)。"""
# 优先用筛选传入的管理类型;否则自动匹配名称含“违约”
domain = []
penalty_types = self.env['yuthon.management.type'].search([('name', 'ilike', '违约')])
keyword = params.get("keyword")
if keyword:
domain += [
'|', '|',
('contract_id.contract_code', 'ilike', keyword),
('property_name', 'ilike', keyword),
('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
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))
if partner_id:
domain.append(('precinct_id', '=', partner_id))
if company_id:
domain.append(('company_id', '=', company_id))
if management_type_id:
domain.append(('management_type_id', '=', management_type_id))
elif penalty_types:
# 兼容未标记管理类型但标记了违约金的记录
domain += ['|', ('management_type_id', 'in', penalty_types.ids), ('is_penalty', '=', True)]
else:
domain.append(('is_penalty', '=', True))
date_domain = []
if start_dt and end_dt:
# 区间匹配 due_date 或 payout_date 任一落在范围内
date_domain = [
'|',
'&', ('due_date', '>=', start_dt), ('due_date', '<=', end_dt),
'&', ('payout_date', '>=', start_dt), ('payout_date', '<=', end_dt),
]
elif start_dt:
date_domain = ['|', ('due_date', '>=', start_dt), ('payout_date', '>=', start_dt)]
elif end_dt:
date_domain = ['|', ('due_date', '<=', end_dt), ('payout_date', '<=', end_dt)]
# 先按日期过滤查,如果无结果且传了日期,则去掉日期过滤再查一遍,避免漏数据
receivables = self.search(domain + date_domain, limit=2000, order="due_date asc")
if not receivables and date_domain:
receivables = self.search(domain, limit=2000, order="due_date asc")
rows = []
total_amount = total_penalty = total_owed = total_collected = total_uncollected = 0.0
total_per_day = 0.0
for idx, rec in enumerate(receivables, start=1):
per_day = rec.per_day or 0.0
penalty_amount = rec.penalty or 0.0
# 违约金计费天数,若每日金额不为0,则用总额/每日
penalty_days = round(penalty_amount / per_day, 2) if per_day else 0.0
amount = rec.original_amount or 0.0
rent_owed = rec.rent_owed or 0.0
collected = rec.delivered1 or 0.0
uncollected = rec.submitted or 0.0
total_amount += amount
total_penalty += penalty_amount
total_owed += rent_owed
total_collected += collected
total_uncollected += uncollected
total_per_day += per_day
rows.append({
"序号": idx,
"合同编号": rec.contract_id.contract_code if rec.contract_id else "",
"物业名称": rec.property_name or "",
"承租方名称": rec.lessee_id.name if rec.lessee_id else "",
"金额": round(amount, 2),
"违约金计费时间(天)": penalty_days,
"欠租金额": round(rent_owed, 2),
"每天违约金(元/天)": round(per_day, 2),
"违约金金额": round(penalty_amount, 2),
"总已收金额": round(collected, 2),
"总未收金额": round(uncollected, 2),
"当月收款时间": rec.payout_date.strftime("%Y-%m-%d") if rec.payout_date else "",
"当月收款金额": round(collected, 2),
"所属公司": rec.company_id.referred if rec.company_id and hasattr(rec.company_id, 'referred') else rec.company_id.name if rec.company_id else "",
"产权证号": rec.title_deed or "",
"管理员": rec.admin_id.name if rec.admin_id else "",
})
return {
"rows": rows,
"summary": {
"序号": False,
"合同编号": "合计",
"物业名称": False,
"承租方名称": False,
"金额": round(total_amount, 2),
"违约金计费时间(天)": False,
"欠租金额": round(total_owed, 2),
"每天违约金(元/天)": round(total_per_day, 2),
"违约金金额": round(total_penalty, 2),
"总已收金额": round(total_collected, 2),
"总未收金额": round(total_uncollected, 2),
"当月收款时间": False,
"当月收款金额": round(total_collected, 2),
"所属公司": False,
"产权证号": False,
"管理员": False,
}
}
@@ -0,0 +1,87 @@
import datetime
from odoo import api, models
class ReceivableDetailReport(models.Model):
_inherit = 'property.accounts.receivable'
@api.model
def get_receivable_detail_report(self, params):
"""
应收明细报表:
- 以应收期间过滤:due_date / due_date_end
- 支持关键词、权属类型、片区、公司、收款类型
- 金额取 original_amount
"""
domain = []
keyword = params.get("keyword")
if keyword:
domain += [
'|', '|',
('contract_id.contract_code', 'ilike', keyword),
('property_name', 'ilike', keyword),
('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))
if partner_id:
domain.append(('precinct_id', '=', partner_id))
if company_id:
domain.append(('company_id', '=', company_id))
if management_type_id:
domain.append(('management_type_id', '=', management_type_id))
receivables = self.search(domain, limit=3000, order="due_date asc")
rows = []
total_amount = 0.0
for idx, rec in enumerate(receivables, start=1):
amount = rec.original_amount or 0.0
total_amount += amount
if rec.due_date_end:
period = f"{rec.due_date}~{rec.due_date_end}"
else:
period = rec.due_date.strftime("%Y-%m-%d") if rec.due_date else ""
rows.append({
"序号": idx,
"应收租期": period,
"承租方名称": rec.lessee_id.name if rec.lessee_id else "",
"金额": round(amount, 2),
"物业名称": rec.property_name or "",
"产权证": rec.title_deed or "",
"合同编号": rec.contract_id.contract_code if rec.contract_id else "",
})
return {
"rows": rows,
"summary": {
"序号": False,
"应收租期": "合计",
"承租方名称": False,
"金额": round(total_amount, 2),
"物业名称": False,
"产权证": False,
"合同编号": False,
}
}
@@ -0,0 +1,73 @@
from odoo import api, models
class RentableAreaReport(models.Model):
_inherit = 'yuthon.property'
@api.model
def get_rentable_area_report(self, params):
"""
可租面积报表:
- 金额字段用 rent_area
- 物业状态取 property_state 映射
- 空置时长取 vacant_duration
"""
domain = []
keyword = params.get("keyword")
if keyword:
domain += [
'|', '|',
('name', 'ilike', keyword),
('title_deed', 'ilike', keyword),
('code', 'ilike', keyword),
]
condition = params.get('condition')
partner_id = params.get('partner_id')
company_id = params.get('company_id')
if condition:
domain.append(('tenure_type', '=', condition))
if partner_id:
domain.append(('precinct_id', '=', partner_id))
if company_id:
domain.append(('company_id', '=', company_id))
properties = self.search(domain, limit=3000)
state_map = {
'on_rent': '已租',
'vacant': '空置',
}
rows = []
total_area = 0.0
for idx, prop in enumerate(properties, start=1):
area = prop.rent_area or 0.0
total_area += area
rows.append({
"序号": idx,
"物业ID": prop.id,
"物业名称": prop.name or "",
"面积": round(area, 4),
"物业状态": state_map.get(prop.property_state, ""),
"空置时长": prop.vacant_duration or 0,
"管理员": prop.admin_id.name if prop.admin_id else "",
"所属公司": prop.company_id.referred if prop.company_id and hasattr(prop.company_id, 'referred') else prop.company_id.name if prop.company_id else "",
})
return {
"rows": rows,
"summary": {
"序号": False,
"物业ID": False,
"物业名称": "合计",
"面积": round(total_area, 4),
"物业状态": False,
"空置时长": False,
"管理员": False,
"所属公司": False,
}
}
@@ -0,0 +1,86 @@
from odoo import api, models
class RentedAreaReport(models.Model):
_inherit = 'yuthon.property'
@api.model
def get_rented_area_report(self, params):
"""
已租面积报表:
- 数据来源:物业表(yuthon.property)
- 仅筛选 property_state = 'on_rent'
- 面积取 rent_area
- 单价取 unit_price(若无则0)
- 租金取当前合同 rent(若无则 current_rent 或0)
"""
domain = [('property_state', '=', 'on_rent')]
keyword = params.get("keyword")
if keyword:
domain += [
'|', '|',
('name', 'ilike', keyword),
('title_deed', 'ilike', keyword),
('contract_id.contract_code', 'ilike', keyword),
]
condition = params.get('condition')
partner_id = params.get('partner_id')
company_id = params.get('company_id')
if condition:
domain.append(('tenure_type', '=', condition))
if partner_id:
domain.append(('precinct_id', '=', partner_id))
if company_id:
domain.append(('company_id', '=', company_id))
properties = self.search(domain, limit=3000)
rows = []
total_area = 0.0
total_rent = 0.0
for idx, prop in enumerate(properties, start=1):
area = prop.rent_area or 0.0
unit_price = prop.unit_price or 0.0
# 物业上未必有 contract_id 字段,安全获取
contract = getattr(prop, 'contract_id', False)
rent_val = 0.0
if contract and getattr(contract, 'rent', False):
rent_val = contract.rent
elif getattr(prop, 'current_rent', False):
rent_val = prop.current_rent
total_area += area
total_rent += rent_val
rows.append({
"序号": idx,
"合同编号": contract.contract_code if contract and getattr(contract, 'contract_code', False) else "",
"物业名称": prop.name or "",
"承租方名称": prop.lessee_id.name if prop.lessee_id else "",
"面积": round(area, 4),
"单价": round(unit_price, 2),
"租金": round(rent_val, 2),
"管理员": prop.admin_id.name if prop.admin_id else "",
"所属公司": prop.company_id.referred if prop.company_id and hasattr(prop.company_id, 'referred') else prop.company_id.name if prop.company_id else "",
})
return {
"rows": rows,
"summary": {
"序号": False,
"合同编号": False,
"物业名称": "合计:",
"承租方名称": False,
"面积": round(total_area, 4),
"单价": False,
"租金": round(total_rent, 2),
"管理员": False,
"所属公司": False,
}
}
@@ -0,0 +1,107 @@
import datetime
from odoo import api, models
class YearlyAssessmentFeeReport(models.Model):
_inherit = 'property.accounts.receivable'
@api.model
def get_yearly_assessment_fee_report(self, params):
"""
本年累计-评估费用:
- 默认统计当年,支持 date_start/date_end 覆盖
- 管理类型默认匹配名称含“评估/估”,可传 management_type_id 精确指定
- 金额取 original_amount
- 日期优先收款日期 payout_date,若无则应收日期 due_date
- 备注取 remark
"""
domain = []
keyword = params.get("keyword")
if keyword:
domain += [
'|', '|',
('contract_id.contract_code', 'ilike', keyword),
('property_name', 'ilike', keyword),
('lessee_id.name', 'ilike', keyword),
]
# 时间范围:默认当年
today = datetime.date.today()
default_start = datetime.date(today.year, 1, 1)
default_end = datetime.date(today.year, 12, 31)
date_start = params.get("date_start")
date_end = params.get("date_end")
start_dt = datetime.datetime.fromisoformat(date_start).date() if date_start else default_start
end_dt = datetime.datetime.fromisoformat(date_end).date() if date_end else default_end
date_domain = [
'|',
'&', ('payout_date', '>=', start_dt), ('payout_date', '<=', end_dt),
'&', ('payout_date', '=', False), ('due_date', '>=', start_dt), ('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')
base_domain = []
if condition:
base_domain.append(('tenure_type', '=', condition))
if partner_id:
base_domain.append(('precinct_id', '=', partner_id))
if company_id:
base_domain.append(('company_id', '=', company_id))
if management_type_id:
type_domain = base_domain + [('management_type_id', '=', management_type_id)]
else:
assess_types = self.env['yuthon.management.type'].search([
'|',
('name', 'ilike', '评估'),
('name', 'ilike', '估'),
])
if assess_types:
type_domain = base_domain + [('management_type_id', 'in', assess_types.ids)]
else:
type_domain = base_domain[:]
receivables = self.search(type_domain + date_domain, limit=3000, order="payout_date asc, due_date asc")
if not receivables and type_domain != base_domain:
receivables = self.search(base_domain + date_domain, limit=3000, order="payout_date asc, due_date asc")
if not receivables and type_domain != base_domain:
receivables = self.search(base_domain, limit=3000, order="payout_date asc, due_date asc")
rows = []
total_amount = 0.0
for idx, rec in enumerate(receivables, start=1):
amount = rec.original_amount or 0.0
total_amount += amount
pay_date = rec.payout_date or rec.due_date
rows.append({
"序号": idx,
"编码": rec.contract_id.contract_code if rec.contract_id else rec.code if hasattr(rec, 'code') else "",
"日期": pay_date.strftime("%Y-%m-%d") if pay_date else "",
"物业名称": rec.property_name or "",
"管理员": rec.admin_id.name if rec.admin_id else "",
"金额": round(amount, 2),
"所属公司": rec.company_id.referred if rec.company_id and hasattr(rec.company_id, 'referred') else rec.company_id.name if rec.company_id else "",
"备注": rec.remark or "",
})
return {
"rows": rows,
"summary": {
"序号": False,
"编码": False,
"日期": False,
"物业名称": False,
"管理员": "合计:",
"金额": round(total_amount, 2),
"所属公司": False,
"备注": False,
}
}
@@ -0,0 +1,88 @@
import datetime
from odoo import api, models
class YearlyExpenseReport(models.Model):
_inherit = 'property.accounts.receivable'
@api.model
def get_yearly_expense_report(self, params):
"""
支出本年-合计:
- 默认统计当前自然年的支出,日期优先用收款日期 payout_date,为空则用应收日期 due_date
- 金额取 original_amount
- 备注取 remark
"""
domain = []
keyword = params.get("keyword")
if keyword:
domain += [
'|', '|',
('contract_id.contract_code', 'ilike', keyword),
('property_name', 'ilike', keyword),
('lessee_id.name', 'ilike', keyword),
]
# 年度时间范围
today = datetime.date.today()
default_start = datetime.date(today.year, 1, 1)
default_end = datetime.date(today.year, 12, 31)
date_start = params.get("date_start")
date_end = params.get("date_end")
start_dt = datetime.datetime.fromisoformat(date_start).date() if date_start else default_start
end_dt = datetime.datetime.fromisoformat(date_end).date() if date_end else default_end
# 日期过滤:收款日期优先,为空则用应收日期
date_domain = [
'|',
'&', ('payout_date', '>=', start_dt), ('payout_date', '<=', end_dt),
'&', ('payout_date', '=', False), ('due_date', '>=', start_dt), ('due_date', '<=', end_dt),
]
condition = params.get('condition')
partner_id = params.get('partner_id')
company_id = params.get('company_id')
base_domain = []
if condition:
base_domain.append(('tenure_type', '=', condition))
if partner_id:
base_domain.append(('precinct_id', '=', partner_id))
if company_id:
base_domain.append(('company_id', '=', company_id))
receivables = self.search(base_domain + date_domain, limit=3000, order="payout_date asc, due_date asc")
if not receivables:
receivables = self.search(base_domain, limit=3000, order="payout_date asc, due_date asc")
rows = []
total_amount = 0.0
for idx, rec in enumerate(receivables, start=1):
amount = rec.original_amount or 0.0
total_amount += amount
pay_date = rec.payout_date or rec.due_date
rows.append({
"序号": idx,
"支出日期": pay_date.strftime("%Y-%m-%d") if pay_date else "",
"金额": round(amount, 2),
"物业名称": rec.property_name or "",
"备注": rec.remark or "",
"管理员": rec.admin_id.name if rec.admin_id else "",
"所属公司": rec.company_id.referred if rec.company_id and hasattr(rec.company_id, 'referred') else rec.company_id.name if rec.company_id else "",
})
return {
"rows": rows,
"summary": {
"序号": False,
"支出日期": False,
"金额": round(total_amount, 2),
"物业名称": False,
"备注": False,
"管理员": False,
"所属公司": False,
}
}
@@ -0,0 +1,107 @@
import datetime
from odoo import api, models
class YearlyMaintenanceFeeReport(models.Model):
_inherit = 'property.accounts.receivable'
@api.model
def get_yearly_maintenance_fee_report(self, params):
"""
本年累计-维修费:
- 默认统计当前自然年,可传 date_start/date_end 覆盖
- 管理类型默认匹配“维/维修”,可传 management_type_id 精确指定
- 金额取 original_amount
- 日期优先收款日期 payout_date,为空则应收日期 due_date
- 备注取 remark
"""
domain = []
keyword = params.get("keyword")
if keyword:
domain += [
'|', '|',
('contract_id.contract_code', 'ilike', keyword),
('property_name', 'ilike', keyword),
('lessee_id.name', 'ilike', keyword),
]
# 时间范围:默认当年
today = datetime.date.today()
default_start = datetime.date(today.year, 1, 1)
default_end = datetime.date(today.year, 12, 31)
date_start = params.get("date_start")
date_end = params.get("date_end")
start_dt = datetime.datetime.fromisoformat(date_start).date() if date_start else default_start
end_dt = datetime.datetime.fromisoformat(date_end).date() if date_end else default_end
date_domain = [
'|',
'&', ('payout_date', '>=', start_dt), ('payout_date', '<=', end_dt),
'&', ('payout_date', '=', False), ('due_date', '>=', start_dt), ('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')
base_domain = []
if condition:
base_domain.append(('tenure_type', '=', condition))
if partner_id:
base_domain.append(('precinct_id', '=', partner_id))
if company_id:
base_domain.append(('company_id', '=', company_id))
if management_type_id:
type_domain = base_domain + [('management_type_id', '=', management_type_id)]
else:
maintenance_types = self.env['yuthon.management.type'].search([
'|',
('name', 'ilike', '维'),
('name', 'ilike', '维修'),
])
if maintenance_types:
type_domain = base_domain + [('management_type_id', 'in', maintenance_types.ids)]
else:
type_domain = base_domain[:]
receivables = self.search(type_domain + date_domain, limit=3000, order="payout_date asc, due_date asc")
if not receivables and type_domain != base_domain:
receivables = self.search(base_domain + date_domain, limit=3000, order="payout_date asc, due_date asc")
if not receivables and type_domain != base_domain:
receivables = self.search(base_domain, limit=3000, order="payout_date asc, due_date asc")
rows = []
total_amount = 0.0
for idx, rec in enumerate(receivables, start=1):
amount = rec.original_amount or 0.0
total_amount += amount
pay_date = rec.payout_date or rec.due_date
rows.append({
"序号": idx,
"编码": rec.contract_id.contract_code if rec.contract_id else rec.code if hasattr(rec, 'code') else "",
"支出日期": pay_date.strftime("%Y-%m-%d") if pay_date else "",
"物业名称": rec.property_name or "",
"管理员": rec.admin_id.name if rec.admin_id else "",
"金额": round(amount, 2),
"所属公司": rec.company_id.referred if rec.company_id and hasattr(rec.company_id, 'referred') else rec.company_id.name if rec.company_id else "",
"备注": rec.remark or "",
})
return {
"rows": rows,
"summary": {
"序号": False,
"编码": False,
"支出日期": False,
"物业名称": False,
"管理员": "合计:",
"金额": round(total_amount, 2),
"所属公司": False,
"备注": False,
}
}
@@ -0,0 +1,109 @@
import datetime
from odoo import api, models
class YearlyManagementFeeReport(models.Model):
_inherit = 'property.accounts.receivable'
@api.model
def get_yearly_management_fee_report(self, params):
"""
本年累计-管理费:
- 默认统计当前自然年,支持传入 date_start/date_end 覆盖
- 管理类型默认匹配名称含“管/管理/物管”,可传 management_type_id 精确指定
- 金额取 original_amount
- 日期显示收款日期 payout_date,若无则应收日期 due_date
- 备注取 remark
"""
domain = []
keyword = params.get("keyword")
if keyword:
domain += [
'|', '|',
('contract_id.contract_code', 'ilike', keyword),
('property_name', 'ilike', keyword),
('lessee_id.name', 'ilike', keyword),
]
# 年度时间范围
today = datetime.date.today()
default_start = datetime.date(today.year, 1, 1)
default_end = datetime.date(today.year, 12, 31)
date_start = params.get("date_start")
date_end = params.get("date_end")
start_dt = datetime.datetime.fromisoformat(date_start).date() if date_start else default_start
end_dt = datetime.datetime.fromisoformat(date_end).date() if date_end else default_end
# 日期过滤:收款日期优先,为空则用应收日期
date_domain = [
'|',
'&', ('payout_date', '>=', start_dt), ('payout_date', '<=', end_dt),
'&', ('payout_date', '=', False), ('due_date', '>=', start_dt), ('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')
base_domain = []
if condition:
base_domain.append(('tenure_type', '=', condition))
if partner_id:
base_domain.append(('precinct_id', '=', partner_id))
if company_id:
base_domain.append(('company_id', '=', company_id))
if management_type_id:
type_domain = base_domain + [('management_type_id', '=', management_type_id)]
else:
mgmt_types = self.env['yuthon.management.type'].search([
'|', '|',
('name', 'ilike', '管'),
('name', 'ilike', '管理'),
('name', 'ilike', '物管'),
])
if mgmt_types:
type_domain = base_domain + [('management_type_id', 'in', mgmt_types.ids)]
else:
type_domain = base_domain[:]
receivables = self.search(type_domain + date_domain, limit=2000, order="payout_date asc, due_date asc")
if not receivables and type_domain != base_domain:
receivables = self.search(base_domain + date_domain, limit=2000, order="payout_date asc, due_date asc")
if not receivables and type_domain != base_domain:
receivables = self.search(base_domain, limit=2000, order="payout_date asc, due_date asc")
rows = []
total_amount = 0.0
for idx, rec in enumerate(receivables, start=1):
amount = rec.original_amount or 0.0
total_amount += amount
pay_date = rec.payout_date or rec.due_date
rows.append({
"序号": idx,
"编码": rec.contract_id.contract_code if rec.contract_id else rec.code if hasattr(rec, 'code') else "",
"支出日期": pay_date.strftime("%Y-%m-%d") if pay_date else "",
"物业名称": rec.property_name or "",
"管理员": rec.admin_id.name if rec.admin_id else "",
"金额": round(amount, 2),
"所属公司": rec.company_id.referred if rec.company_id and hasattr(rec.company_id, 'referred') else rec.company_id.name if rec.company_id else "",
"备注": rec.remark or "",
})
return {
"rows": rows,
"summary": {
"序号": False,
"编码": False,
"支出日期": False,
"物业名称": False,
"管理员": "合计:",
"金额": round(total_amount, 2),
"所属公司": False,
"备注": False,
}
}
@@ -0,0 +1,107 @@
import datetime
from odoo import api, models
class YearlyOtherFeeReport(models.Model):
_inherit = 'property.accounts.receivable'
@api.model
def get_yearly_other_fee_report(self, params):
"""
本年累计-其他费用:
- 默认统计当年,支持 date_start/date_end 覆盖
- 管理类型默认匹配名称含“其他/其它”,可传 management_type_id 精确指定
- 金额取 original_amount
- 日期优先 payout_date,为空则 due_date
- 备注取 remark
"""
domain = []
keyword = params.get("keyword")
if keyword:
domain += [
'|', '|',
('contract_id.contract_code', 'ilike', keyword),
('property_name', 'ilike', keyword),
('lessee_id.name', 'ilike', keyword),
]
# 年度范围
today = datetime.date.today()
default_start = datetime.date(today.year, 1, 1)
default_end = datetime.date(today.year, 12, 31)
date_start = params.get("date_start")
date_end = params.get("date_end")
start_dt = datetime.datetime.fromisoformat(date_start).date() if date_start else default_start
end_dt = datetime.datetime.fromisoformat(date_end).date() if date_end else default_end
date_domain = [
'|',
'&', ('payout_date', '>=', start_dt), ('payout_date', '<=', end_dt),
'&', ('payout_date', '=', False), ('due_date', '>=', start_dt), ('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')
base_domain = []
if condition:
base_domain.append(('tenure_type', '=', condition))
if partner_id:
base_domain.append(('precinct_id', '=', partner_id))
if company_id:
base_domain.append(('company_id', '=', company_id))
if management_type_id:
type_domain = base_domain + [('management_type_id', '=', management_type_id)]
else:
other_types = self.env['yuthon.management.type'].search([
'|',
('name', 'ilike', '其他'),
('name', 'ilike', '其它'),
])
if other_types:
type_domain = base_domain + [('management_type_id', 'in', other_types.ids)]
else:
type_domain = base_domain[:]
receivables = self.search(type_domain + date_domain, limit=3000, order="payout_date asc, due_date asc")
if not receivables and type_domain != base_domain:
receivables = self.search(base_domain + date_domain, limit=3000, order="payout_date asc, due_date asc")
if not receivables and type_domain != base_domain:
receivables = self.search(base_domain, limit=3000, order="payout_date asc, due_date asc")
rows = []
total_amount = 0.0
for idx, rec in enumerate(receivables, start=1):
amount = rec.original_amount or 0.0
total_amount += amount
pay_date = rec.payout_date or rec.due_date
rows.append({
"序号": idx,
"编码": rec.contract_id.contract_code if rec.contract_id else rec.code if hasattr(rec, 'code') else "",
"支出日期": pay_date.strftime("%Y-%m-%d") if pay_date else "",
"物业名称": rec.property_name or "",
"管理员": rec.admin_id.name if rec.admin_id else "",
"金额": round(amount, 2),
"所属公司": rec.company_id.referred if rec.company_id and hasattr(rec.company_id, 'referred') else rec.company_id.name if rec.company_id else "",
"备注": rec.remark or "",
})
return {
"rows": rows,
"summary": {
"序号": False,
"编码": False,
"支出日期": False,
"物业名称": False,
"管理员": "合计:",
"金额": round(total_amount, 2),
"所属公司": False,
"备注": False,
}
}
@@ -0,0 +1,91 @@
import datetime
from odoo import api, models
class YearlyPenaltyReport(models.Model):
_inherit = 'property.accounts.receivable'
@api.model
def get_yearly_penalty_report(self, params):
"""
违约金本年累计:
- 默认过滤当年应收日期,可传 date_start/date_end 覆盖
- 管理类型默认匹配“违约”或 is_penalty=True,也可传 management_type_id 精确指定
- 金额优先取 penalty,其次 submitted,再次 original_amount
"""
domain = []
keyword = params.get("keyword")
if keyword:
domain += [
'|', '|',
('contract_id.contract_code', 'ilike', keyword),
('property_name', 'ilike', keyword),
('lessee_id.name', 'ilike', keyword),
]
# 时间范围:默认当年
today = datetime.date.today()
year_start = datetime.date(today.year, 1, 1)
year_end = datetime.date(today.year, 12, 31)
date_start = params.get("date_start")
date_end = params.get("date_end")
start_dt = datetime.datetime.fromisoformat(date_start).date() if date_start else year_start
end_dt = datetime.datetime.fromisoformat(date_end).date() if date_end else year_end
domain.append(("due_date", ">=", start_dt))
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))
if partner_id:
domain.append(('precinct_id', '=', partner_id))
if company_id:
domain.append(('company_id', '=', company_id))
if management_type_id:
type_domain = domain + [('management_type_id', '=', management_type_id)]
else:
penalty_types = self.env['yuthon.management.type'].search([('name', 'ilike', '违约')])
if penalty_types:
type_domain = domain + ['|', ('management_type_id', 'in', penalty_types.ids), ('is_penalty', '=', True)]
else:
type_domain = domain + [('is_penalty', '=', True)]
receivables = self.search(type_domain, order="due_date asc")
if not receivables and type_domain != domain:
receivables = self.search(domain, order="due_date asc")
rows = []
total_amount = 0.0
for idx, rec in enumerate(receivables, start=1):
amount = rec.penalty or rec.submitted or rec.original_amount or 0.0
total_amount += amount
rows.append({
"序号": idx,
"合同编号": rec.contract_id.contract_code if rec.contract_id else "",
"物业名称": rec.property_name or "",
"承租方名称": rec.lessee_id.name if rec.lessee_id else "",
"金额": round(amount, 2),
"所属公司": rec.company_id.referred if rec.company_id and hasattr(rec.company_id, 'referred') else rec.company_id.name if rec.company_id else "",
"管理员": rec.admin_id.name if rec.admin_id else "",
})
return {
"rows": rows,
"summary": {
"序号": False,
"合同编号": False,
"物业名称": False,
"承租方名称": "合计:",
"金额": round(total_amount, 2),
"所属公司": False,
"管理员": False,
}
}
@@ -50,7 +50,30 @@ class ReceivableReport extends Component {
theme: agGrid.themeMaterial,
columnDefs: this.config.columns || [],
rowData: [],
sideBar: true,
sideBar: {
toolPanels: [
{
id: 'columns',
labelDefault: '列',
labelKey: 'columns',
iconKey: 'columns',
toolPanel: 'agColumnsToolPanel',
toolPanelParams: {
suppressValues: true,
suppressPivots: true,
suppressPivotMode: true,
}
},
{
id: 'filters',
labelDefault: '过滤器',
labelKey: 'filters',
iconKey: 'filter',
toolPanel: 'agFiltersToolPanel'
}
],
defaultToolPanel: '',
},
enableCharts: true,
cellSelection: true,
defaultColDef: {
@@ -59,7 +82,14 @@ class ReceivableReport extends Component {
width: 200,
minWidth: 150,
flex: false,
resizable: false
resizable: false,
valueFormatter: (params) => {
// 如果值为false或null或undefined,返回空字符串
if (params.value === false || params.value === null || params.value === undefined || params.value === 'false') {
return '';
}
return params.value;
}
},
getContextMenuItems(params) {
const items = params.defaultItems.slice();
@@ -102,6 +132,8 @@ class ReceivableReport extends Component {
async onUpdatePartner(v) {
console.log(v)
this.state.selectPartner = v[0]
// 自动搜索
await this.onSearch()
}
async searchUserDomain() {
@@ -142,15 +174,21 @@ class ReceivableReport extends Component {
// 重置 partner 筛选结果
// 修改过滤条件
await this.searchUserDomain()
// 自动搜索
await this.onSearch()
}
onClickPreMonth(){
// 开始的月份的前一个月
this.state.startDate = this.state.startDate.minus({months: 1});
this.state.endDate = this.state.startDate.endOf("month");
// 自动搜索
this.onSearch()
}
onClickNextMonth() {
this.state.startDate = this.state.startDate.plus({months: 1});
this.state.endDate = this.state.startDate.endOf("month");
// 自动搜索
this.onSearch()
}
async onSearch() {
@@ -173,7 +211,16 @@ class ReceivableReport extends Component {
[params]
);
console.log(result)
this.grid.setGridOption('rowData', result);
// 处理返回的数据结构,支持固定底部合计行
if (result && typeof result === 'object' && result.rows && result.summary) {
// 新格式:分离数据行和合计行
this.grid.setGridOption('rowData', result.rows);
this.grid.setGridOption('pinnedBottomRowData', [result.summary]);
} else {
// 兼容旧格式:数组
this.grid.setGridOption('rowData', result);
}
}
onExportExcel() {
@@ -194,6 +241,13 @@ class ReceivableReport extends Component {
async onUpdateManagementType(v) {
this.state.selectManagementType = v[0] || {};
// 自动搜索
await this.onSearch()
}
onChangeCondition() {
// 自动搜索
this.onSearch()
}
async onChangeEndDate(date) {
@@ -3,20 +3,14 @@
<templates id="template" xml:space="preserve">
<t t-name="youthon_report.ReceivableReport">
<div class="container-fluid d-flex flex-column h-100 mt-2 pb-3">
<div class="col-md-4">
<div class="stat-card danger bg-white">
<div class="stat-label mb-2">出租率</div>
<div class="stat-value text-dark">¥245,890.00</div>
</div>
</div>
<!-- 筛选卡片 -->
<div class="card mb-2">
<div class="card-body">
<div class="card-body py-2">
<!-- 搜索区域 -->
<div class="row mb-2" t-ref="search_area">
<div class="row align-items-end" t-ref="search_area">
<!-- 应收日期范围 -->
<div class="col-4">
<label>日期范围</label>
<div class="col-3">
<label class="mb-1">日期范围</label>
<div class="d-flex align-items-center gap-2">
<DateTimeInput
range="true"
@@ -25,74 +19,72 @@
placeholder="fromPlaceholder"
onChange="(date) => this.onChangeStartDate(date)"
/>
<!-- <i class="oi oi-arrow-right"></i>-->
<DateTimeInput
range="true"
range="true"
value="state.endDate"
type="'date'"
placeholder="toPlaceholder"
onChange="(date) => this.onChangeEndDate(date)"
/>
<!-- 上一个月 按钮-->
<button class="btn btn-info" style="margin-bottom: 5px" t-on-click="onClickPreMonth">
<button class="btn btn-info btn-sm" t-on-click="onClickPreMonth">
<i class="oi oi-chevron-left"></i>
</button>
<!-- 下一个月-->
<button class="btn btn-info" style="margin-bottom: 5px" t-on-click="onClickNextMonth">
<button class="btn btn-info btn-sm" t-on-click="onClickNextMonth">
<i class="oi oi-chevron-right"></i>
</button>
</div>
</div>
<!-- 所属公司 -->
<div class="col-2">
<label>所属公司</label>
<div class="col">
<Many2XAutocomplete
value="state.selectCompany?.display_name"
getDomain="getDomainCompany"
resModel="'yuthon.property.company'"
fieldString="'所属公司'"
placeholder="'所属公司'"
activeActions="{}"
update.bind="onUpdateCompany"
/>
</div>
<div class="col-2">
<label>权属类型</label>
<select id="formview_default_conditions" class="o_input" t-model="state.condition">
<div class="col">
<select id="formview_default_conditions" class="o_input" t-model="state.condition" t-on-change="onChangeCondition">
<option t-foreach="state.conditions" t-as="cond"
t-att-value="cond.value"
t-key="cond.name">
<t t-esc="cond.name"/>
</option>
</select>
</div><!-- 片区管理员 -->
<div class="col-2">
<label>片区管理员</label>
</div>
<!-- 片区管理员 -->
<div class="col">
<Many2XAutocomplete
value="state.selectPartner?.display_name"
getDomain.bind="getDomain"
resModel="'yuthon.precinct.message'"
fieldString="'片区管理员'"
placeholder="'片区管理员'"
activeActions="{}"
update.bind="onUpdatePartner"
/>
</div>
<div class="col-2">
<label>收款类型</label>
<div class="col">
<Many2XAutocomplete
value="state.selectManagementType?.display_name"
getDomain="getDomainManagementType"
resModel="'yuthon.management.type'"
fieldString="'收款类型'"
placeholder="'收款类型'"
activeActions="{}"
update.bind="onUpdateManagementType"
/>
</div>
<!-- 操作按钮 -->
<div class="col-12 mt-3 d-flex justify-content-end gap-2">
<button class="btn btn-primary" t-on-click="onSearch">搜索</button>
<div class="col-auto">
<button class="btn btn-success" t-on-click="onExportExcel">导出</button>
</div>
</div>
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="action_cumulative_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": "property.accounts.receivable",
"method": "get_cumulative_arrears_report",
"columns": [
{"headerName": "序号", "field": "序号", "flex": 0.6},
{"headerName": "物业名称", "field": "物业名称", "flex": 2},
{"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}
]
}
}
</field>
</record>
<menuitem
id="menu_cumulative_arrears_report"
name="累计欠收"
parent="yuthon_property_approval.property_approval_report_menu"
action="action_cumulative_arrears_report"
sequence="17"
/>
</data>
</odoo>
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="action_current_month_received_report" model="ir.actions.client">
<field name="name">归属本月实收</field>
<field name="tag">receivable_report_client</field>
<field name="context">
{
"sideBar": true,
"report_config": {
"model": "property.accounts.receivable",
"method": "get_current_month_received_report",
"columns": [
{"headerName": "序号", "field": "序号", "flex": 0.6},
{"headerName": "合同编号", "field": "合同编号", "flex": 1},
{"headerName": "物业名称", "field": "物业名称", "flex": 1.6},
{"headerName": "承租方名称", "field": "承租方名称", "flex": 1.2},
{"headerName": "金额", "field": "金额", "flex": 1, "type": "number"},
{"headerName": "所属公司", "field": "所属公司", "flex": 1},
{"headerName": "管理员", "field": "管理员", "flex": 1}
]
}
}
</field>
</record>
<menuitem
id="menu_current_month_received_report"
name="归属本月实收"
parent="yuthon_property_approval.property_approval_report_menu"
action="action_current_month_received_report"
sequence="14"
/>
</data>
</odoo>
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="action_current_receivable_property_fee_report" model="ir.actions.client">
<field name="name">当期应收-物管水电费</field>
<field name="tag">receivable_report_client</field>
<field name="context">
{
"sideBar": true,
"report_config": {
"model": "property.accounts.receivable",
"method": "get_current_receivable_property_fee_report",
"columns": [
{"headerName": "序号", "field": "序号", "flex": 0.6},
{"headerName": "应收租期", "field": "应收租期", "flex": 1.4},
{"headerName": "承租方名称", "field": "承租方名称", "flex": 1.6},
{"headerName": "金额(含税)", "field": "金额(含税)", "flex": 1, "type": "number"},
{"headerName": "税率", "field": "税率", "flex": 0.8},
{"headerName": "金额(不含税)", "field": "金额(不含税)", "flex": 1, "type": "number"},
{"headerName": "物业名称", "field": "物业名称", "flex": 2},
{"headerName": "产权证", "field": "产权证", "flex": 1.2},
{"headerName": "合同编号", "field": "合同编号", "flex": 1.2},
{"headerName": "管理员", "field": "管理员", "flex": 1}
]
}
}
</field>
</record>
<menuitem
id="menu_current_receivable_property_fee_report"
name="当期应收-物管水电费"
parent="yuthon_property_approval.property_approval_report_menu"
action="action_current_receivable_property_fee_report"
sequence="20"
/>
</data>
</odoo>
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="action_current_receivable_rent_report" model="ir.actions.client">
<field name="name">当期应收-租金</field>
<field name="tag">receivable_report_client</field>
<field name="context">
{
"sideBar": true,
"report_config": {
"model": "property.accounts.receivable",
"method": "get_current_receivable_rent_report",
"columns": [
{"headerName": "序号", "field": "序号", "flex": 0.6, "valueFormatter": "params.value === false || params.value === null || params.value === undefined || params.value === 'false' ? '' : params.value"},
{"headerName": "应收开始", "field": "应收开始", "flex": 1.2},
{"headerName": "应收结束", "field": "应收结束", "flex": 1.2},
{"headerName": "承租方名称", "field": "承租方名称", "flex": 1.6},
{"headerName": "金额(含税)", "field": "金额(含税)", "flex": 1, "type": "number"},
{"headerName": "税率", "field": "税率", "flex": 0.8, "valueFormatter": "params.value === false || params.value === null || params.value === undefined || params.value === 'false' ? '' : params.value"},
{"headerName": "金额(不含税)", "field": "金额(不含税)", "flex": 1, "type": "number"},
{"headerName": "物业名称", "field": "物业名称", "flex": 2},
{"headerName": "产权证", "field": "产权证", "flex": 1.2},
{"headerName": "合同编号", "field": "合同编号", "flex": 1.2},
{"headerName": "管理员", "field": "管理员", "flex": 1}
],
"getRowStyle": "function(params) { if (params.data.acc_type === 'new') { return { color: '#28a745' }; } return null; }"
}
}
</field>
</record>
<menuitem
id="menu_current_receivable_rent_report"
name="当期应收-租金"
parent="yuthon_property_approval.property_approval_report_menu"
action="action_current_receivable_rent_report"
sequence="19"
/>
</data>
</odoo>
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="action_deposit_total_report" model="ir.actions.client">
<field name="name">押金累计总额</field>
<field name="tag">receivable_report_client</field>
<field name="context">
{
"sideBar": true,
"report_config": {
"model": "property.accounts.receivable",
"method": "get_deposit_total_report",
"columns": [
{"headerName": "序号", "field": "序号", "flex": 0.6},
{"headerName": "合同编号", "field": "合同编号", "flex": 1.2},
{"headerName": "物业名称", "field": "物业名称", "flex": 2},
{"headerName": "承租方名称", "field": "承租方名称", "flex": 1.5},
{"headerName": "金额", "field": "金额", "flex": 1, "type": "number"},
{"headerName": "所属公司", "field": "所属公司", "flex": 1},
{"headerName": "管理员", "field": "管理员", "flex": 1}
]
}
}
</field>
</record>
<menuitem
id="menu_deposit_total_report"
name="押金累计总额"
parent="yuthon_property_approval.property_approval_report_menu"
action="action_deposit_total_report"
sequence="24"
/>
</data>
</odoo>
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="action_management_area_report" model="ir.actions.client">
<field name="name">管理面积报表</field>
<field name="tag">receivable_report_client</field>
<field name="context">
{
"sideBar": true,
"report_config": {
"model": "yuthon.property",
"method": "get_management_area_report",
"columns": [
{"headerName": "序号", "field": "序号", "flex": 0.6},
{"headerName": "物业id", "field": "物业id", "flex": 0.8},
{"headerName": "产权证号", "field": "产权证号", "flex": 1.2},
{"headerName": "物业名称", "field": "物业名称", "flex": 1.8},
{"headerName": "可租面积", "field": "可租面积", "flex": 1, "type": "number"},
{"headerName": "产权面积", "field": "产权面积", "flex": 1, "type": "number"},
{"headerName": "用途", "field": "用途", "flex": 1},
{"headerName": "管理费标准", "field": "管理费标准", "flex": 1}
]
}
}
</field>
</record>
<menuitem
id="menu_management_area_report"
name="管理面积报表"
parent="yuthon_property_approval.property_approval_report_menu"
action="action_management_area_report"
sequence="12"
/>
</data>
</odoo>
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="action_monthly_assessment_fee_report" model="ir.actions.client">
<field name="name">本月支出-评估费</field>
<field name="tag">receivable_report_client</field>
<field name="context">
{
"sideBar": true,
"report_config": {
"model": "property.accounts.receivable",
"method": "get_monthly_assessment_fee_report",
"columns": [
{"headerName": "序号", "field": "序号", "flex": 0.6},
{"headerName": "编码", "field": "编码", "flex": 1},
{"headerName": "日期", "field": "日期", "flex": 1.2},
{"headerName": "物业名称", "field": "物业名称", "flex": 2},
{"headerName": "金额", "field": "金额", "flex": 1, "type": "number"},
{"headerName": "备注", "field": "备注", "flex": 2},
{"headerName": "所属公司", "field": "所属公司", "flex": 1},
{"headerName": "管理员", "field": "管理员", "flex": 1}
]
}
}
</field>
</record>
<menuitem
id="menu_monthly_assessment_fee_report"
name="本月支出-评估费"
parent="yuthon_property_approval.property_approval_report_menu"
action="action_monthly_assessment_fee_report"
sequence="33"
/>
</data>
</odoo>
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="action_monthly_expense_total_report" model="ir.actions.client">
<field name="name">本月支出-合计</field>
<field name="tag">receivable_report_client</field>
<field name="context">
{
"sideBar": true,
"report_config": {
"model": "property.accounts.receivable",
"method": "get_monthly_expense_total_report",
"columns": [
{"headerName": "序号", "field": "序号", "flex": 0.6},
{"headerName": "编码", "field": "编码", "flex": 1},
{"headerName": "日期", "field": "日期", "flex": 1.2},
{"headerName": "物业名称", "field": "物业名称", "flex": 2},
{"headerName": "金额", "field": "金额", "flex": 1, "type": "number"},
{"headerName": "备注", "field": "备注", "flex": 2},
{"headerName": "所属公司", "field": "所属公司", "flex": 1},
{"headerName": "管理员", "field": "管理员", "flex": 1}
]
}
}
</field>
</record>
<menuitem
id="menu_monthly_expense_total_report"
name="本月支出-合计"
parent="yuthon_property_approval.property_approval_report_menu"
action="action_monthly_expense_total_report"
sequence="32"
/>
</data>
</odoo>
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="action_monthly_maintenance_fee_report" model="ir.actions.client">
<field name="name">本月支出-维修费</field>
<field name="tag">receivable_report_client</field>
<field name="context">
{
"sideBar": true,
"report_config": {
"model": "property.accounts.receivable",
"method": "get_monthly_maintenance_fee_report",
"columns": [
{"headerName": "序号", "field": "序号", "flex": 0.6},
{"headerName": "编码", "field": "编码", "flex": 1},
{"headerName": "日期", "field": "日期", "flex": 1.2},
{"headerName": "物业名称", "field": "物业名称", "flex": 2},
{"headerName": "金额", "field": "金额", "flex": 1, "type": "number"},
{"headerName": "备注", "field": "备注", "flex": 2},
{"headerName": "所属公司", "field": "所属公司", "flex": 1},
{"headerName": "管理员", "field": "管理员", "flex": 1}
]
}
}
</field>
</record>
<menuitem
id="menu_monthly_maintenance_fee_report"
name="本月支出-维修费"
parent="yuthon_property_approval.property_approval_report_menu"
action="action_monthly_maintenance_fee_report"
sequence="35"
/>
</data>
</odoo>
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="action_monthly_management_fee_report" model="ir.actions.client">
<field name="name">本月支出-管理费</field>
<field name="tag">receivable_report_client</field>
<field name="context">
{
"sideBar": true,
"report_config": {
"model": "property.accounts.receivable",
"method": "get_monthly_management_fee_report",
"columns": [
{"headerName": "序号", "field": "序号", "flex": 0.6},
{"headerName": "编码", "field": "编码", "flex": 1},
{"headerName": "日期", "field": "日期", "flex": 1.2},
{"headerName": "物业名称", "field": "物业名称", "flex": 2},
{"headerName": "金额", "field": "金额", "flex": 1, "type": "number"},
{"headerName": "备注", "field": "备注", "flex": 2},
{"headerName": "所属公司", "field": "所属公司", "flex": 1},
{"headerName": "管理员", "field": "管理员", "flex": 1}
]
}
}
</field>
</record>
<menuitem
id="menu_monthly_management_fee_report"
name="本月支出-管理费"
parent="yuthon_property_approval.property_approval_report_menu"
action="action_monthly_management_fee_report"
sequence="31"
/>
</data>
</odoo>
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="action_monthly_other_expense_report" model="ir.actions.client">
<field name="name">本月支出-其他</field>
<field name="tag">receivable_report_client</field>
<field name="context">
{
"sideBar": true,
"report_config": {
"model": "property.accounts.receivable",
"method": "get_monthly_other_expense_report",
"columns": [
{"headerName": "序号", "field": "序号", "flex": 0.6},
{"headerName": "编码", "field": "编码", "flex": 1},
{"headerName": "日期", "field": "日期", "flex": 1.2},
{"headerName": "物业名称", "field": "物业名称", "flex": 2},
{"headerName": "金额", "field": "金额", "flex": 1, "type": "number"},
{"headerName": "备注", "field": "备注", "flex": 2},
{"headerName": "所属公司", "field": "所属公司", "flex": 1},
{"headerName": "管理员", "field": "管理员", "flex": 1}
]
}
}
</field>
</record>
<menuitem
id="menu_monthly_other_expense_report"
name="本月支出-其他"
parent="yuthon_property_approval.property_approval_report_menu"
action="action_monthly_other_expense_report"
sequence="34"
/>
</data>
</odoo>
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="action_parking_fee_current_month_receivable" model="ir.actions.client">
<field name="name">停车费本月应收</field>
<field name="tag">receivable_report_client</field>
<field name="context">
{
"sideBar": true,
"report_config": {
"model": "property.accounts.receivable",
"method": "get_parking_fee_current_month_receivable",
"columns": [
{"headerName": "序号", "field": "序号", "flex": 0.6},
{"headerName": "合同编号", "field": "合同编号", "flex": 1.2},
{"headerName": "物业名称", "field": "物业名称", "flex": 2},
{"headerName": "承租方名称", "field": "承租方名称", "flex": 1.5},
{"headerName": "金额", "field": "金额", "flex": 1, "type": "number"},
{"headerName": "所属公司", "field": "所属公司", "flex": 1},
{"headerName": "管理员", "field": "管理员", "flex": 1}
]
}
}
</field>
</record>
<menuitem
id="menu_parking_fee_current_month_receivable"
name="停车费本月应收"
parent="yuthon_property_approval.property_approval_report_menu"
action="action_parking_fee_current_month_receivable"
sequence="22"
/>
</data>
</odoo>
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="action_parking_fee_current_month_report" model="ir.actions.client">
<field name="name">停车费本月实收</field>
<field name="tag">receivable_report_client</field>
<field name="context">
{
"sideBar": true,
"report_config": {
"model": "property.accounts.receivable",
"method": "get_parking_fee_current_month_report",
"columns": [
{"headerName": "序号", "field": "序号", "flex": 0.6},
{"headerName": "合同编号", "field": "合同编号", "flex": 1.2},
{"headerName": "物业名称", "field": "物业名称", "flex": 2},
{"headerName": "承租方名称", "field": "承租方名称", "flex": 1.5},
{"headerName": "金额", "field": "金额", "flex": 1, "type": "number"},
{"headerName": "所属公司", "field": "所属公司", "flex": 1},
{"headerName": "管理员", "field": "管理员", "flex": 1}
]
}
}
</field>
</record>
<menuitem
id="menu_parking_fee_current_month_report"
name="停车费本月实收"
parent="yuthon_property_approval.property_approval_report_menu"
action="action_parking_fee_current_month_report"
sequence="21"
/>
</data>
</odoo>
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="action_parking_fee_yearly_report" model="ir.actions.client">
<field name="name">停车费本年累计</field>
<field name="tag">receivable_report_client</field>
<field name="context">
{
"sideBar": true,
"report_config": {
"model": "property.accounts.receivable",
"method": "get_parking_fee_yearly_report",
"columns": [
{"headerName": "序号", "field": "序号", "flex": 0.6},
{"headerName": "合同编号", "field": "合同编号", "flex": 1.2},
{"headerName": "物业名称", "field": "物业名称", "flex": 2},
{"headerName": "承租方名称", "field": "承租方名称", "flex": 1.5},
{"headerName": "金额", "field": "金额", "flex": 1, "type": "number"},
{"headerName": "所属公司", "field": "所属公司", "flex": 1},
{"headerName": "管理员", "field": "管理员", "flex": 1}
]
}
}
</field>
</record>
<menuitem
id="menu_parking_fee_yearly_report"
name="停车费本年累计"
parent="yuthon_property_approval.property_approval_report_menu"
action="action_parking_fee_yearly_report"
sequence="18"
/>
</data>
</odoo>
@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="action_penalty_detail_report" model="ir.actions.client">
<field name="name">违约金明细报表</field>
<field name="tag">receivable_report_client</field>
<field name="context">
{
"sideBar": true,
"report_config": {
"model": "property.accounts.receivable",
"method": "get_penalty_detail_report",
"columns": [
{"headerName": "序号", "field": "序号", "flex": 0.6},
{"headerName": "合同编号", "field": "合同编号", "flex": 1},
{"headerName": "物业名称", "field": "物业名称", "flex": 1.6},
{"headerName": "承租方名称", "field": "承租方名称", "flex": 1.2},
{"headerName": "金额", "field": "金额", "flex": 1, "type": "number"},
{"headerName": "违约金计费时间(天)", "field": "违约金计费时间(天)", "flex": 1},
{"headerName": "欠租金额", "field": "欠租金额", "flex": 1, "type": "number"},
{"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},
{"headerName": "当月收款金额", "field": "当月收款金额", "flex": 1, "type": "number"},
{"headerName": "所属公司", "field": "所属公司", "flex": 1},
{"headerName": "产权证号", "field": "产权证号", "flex": 1},
{"headerName": "管理员", "field": "管理员", "flex": 1}
]
}
}
</field>
</record>
<menuitem
id="menu_penalty_detail_report"
name="违约金明细报表"
parent="yuthon_property_approval.property_approval_report_menu"
action="action_penalty_detail_report"
sequence="13"
/>
</data>
</odoo>
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="action_receivable_detail_report" model="ir.actions.client">
<field name="name">合同应收</field>
<field name="tag">receivable_report_client</field>
<field name="context">
{
"sideBar": true,
"report_config": {
"model": "property.accounts.receivable",
"method": "get_receivable_detail_report",
"columns": [
{"headerName": "序号", "field": "序号", "flex": 0.6},
{"headerName": "应收租期", "field": "应收租期", "flex": 1.4},
{"headerName": "承租方名称", "field": "承租方名称", "flex": 1.4},
{"headerName": "金额", "field": "金额", "flex": 1, "type": "number"},
{"headerName": "物业名称", "field": "物业名称", "flex": 1.8},
{"headerName": "产权证", "field": "产权证", "flex": 1.2},
{"headerName": "合同编号", "field": "合同编号", "flex": 1.2}
]
}
}
</field>
</record>
<menuitem
id="menu_receivable_detail_report"
name="合同应收"
parent="yuthon_property_approval.property_approval_report_menu"
action="action_receivable_detail_report"
sequence="15"
/>
</data>
</odoo>
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="action_rentable_area_report" model="ir.actions.client">
<field name="name">可租面积</field>
<field name="tag">receivable_report_client</field>
<field name="context">
{
"sideBar": true,
"report_config": {
"model": "yuthon.property",
"method": "get_rentable_area_report",
"columns": [
{"headerName": "序号", "field": "序号", "flex": 0.6},
{"headerName": "物业ID", "field": "物业ID", "flex": 0.8},
{"headerName": "物业名称", "field": "物业名称", "flex": 2},
{"headerName": "面积", "field": "面积", "flex": 1, "type": "number"},
{"headerName": "物业状态", "field": "物业状态", "flex": 1},
{"headerName": "空置时长", "field": "空置时长", "flex": 1},
{"headerName": "管理员", "field": "管理员", "flex": 1},
{"headerName": "所属公司", "field": "所属公司", "flex": 1}
]
}
}
</field>
</record>
<menuitem
id="menu_rentable_area_report"
name="可租面积"
parent="yuthon_property_approval.property_approval_report_menu"
action="action_rentable_area_report"
sequence="16"
/>
</data>
</odoo>
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="action_rented_area_report" model="ir.actions.client">
<field name="name">已租面积</field>
<field name="tag">receivable_report_client</field>
<field name="context">
{
"sideBar": true,
"report_config": {
"model": "yuthon.property",
"method": "get_rented_area_report",
"columns": [
{"headerName": "序号", "field": "序号", "flex": 0.6},
{"headerName": "合同编号", "field": "合同编号", "flex": 1.2},
{"headerName": "物业名称", "field": "物业名称", "flex": 2},
{"headerName": "承租方名称", "field": "承租方名称", "flex": 1.5},
{"headerName": "面积", "field": "面积", "flex": 1, "type": "number"},
{"headerName": "单价", "field": "单价", "flex": 1, "type": "number"},
{"headerName": "租金", "field": "租金", "flex": 1, "type": "number"},
{"headerName": "管理员", "field": "管理员", "flex": 1},
{"headerName": "所属公司", "field": "所属公司", "flex": 1}
]
}
}
</field>
</record>
<menuitem
id="menu_rented_area_report"
name="已租面积"
parent="yuthon_property_approval.property_approval_report_menu"
action="action_rented_area_report"
sequence="25"
/>
</data>
</odoo>
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="action_yearly_assessment_fee_report" model="ir.actions.client">
<field name="name">本年累计-评估费用</field>
<field name="tag">receivable_report_client</field>
<field name="context">
{
"sideBar": true,
"report_config": {
"model": "property.accounts.receivable",
"method": "get_yearly_assessment_fee_report",
"columns": [
{"headerName": "序号", "field": "序号", "flex": 0.6},
{"headerName": "编码", "field": "编码", "flex": 1},
{"headerName": "日期", "field": "日期", "flex": 1.2},
{"headerName": "物业名称", "field": "物业名称", "flex": 2},
{"headerName": "管理员", "field": "管理员", "flex": 1},
{"headerName": "金额", "field": "金额", "flex": 1, "type": "number"},
{"headerName": "所属公司", "field": "所属公司", "flex": 1},
{"headerName": "备注", "field": "备注", "flex": 2}
]
}
}
</field>
</record>
<menuitem
id="menu_yearly_assessment_fee_report"
name="本年累计-评估费用"
parent="yuthon_property_approval.property_approval_report_menu"
action="action_yearly_assessment_fee_report"
sequence="28"
/>
</data>
</odoo>
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="action_yearly_expense_report" model="ir.actions.client">
<field name="name">支出本年-合计</field>
<field name="tag">receivable_report_client</field>
<field name="context">
{
"sideBar": true,
"report_config": {
"model": "property.accounts.receivable",
"method": "get_yearly_expense_report",
"columns": [
{"headerName": "序号", "field": "序号", "flex": 0.6},
{"headerName": "支出日期", "field": "支出日期", "flex": 1.2},
{"headerName": "金额", "field": "金额", "flex": 1, "type": "number"},
{"headerName": "物业名称", "field": "物业名称", "flex": 2},
{"headerName": "备注", "field": "备注", "flex": 2},
{"headerName": "管理员", "field": "管理员", "flex": 1},
{"headerName": "所属公司", "field": "所属公司", "flex": 1}
]
}
}
</field>
</record>
<menuitem
id="menu_yearly_expense_report"
name="支出本年-合计"
parent="yuthon_property_approval.property_approval_report_menu"
action="action_yearly_expense_report"
sequence="27"
/>
</data>
</odoo>
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="action_yearly_maintenance_fee_report" model="ir.actions.client">
<field name="name">本年累计-维修费</field>
<field name="tag">receivable_report_client</field>
<field name="context">
{
"sideBar": true,
"report_config": {
"model": "property.accounts.receivable",
"method": "get_yearly_maintenance_fee_report",
"columns": [
{"headerName": "序号", "field": "序号", "flex": 0.6},
{"headerName": "编码", "field": "编码", "flex": 1},
{"headerName": "支出日期", "field": "支出日期", "flex": 1.2},
{"headerName": "物业名称", "field": "物业名称", "flex": 2},
{"headerName": "管理员", "field": "管理员", "flex": 1},
{"headerName": "金额", "field": "金额", "flex": 1, "type": "number"},
{"headerName": "所属公司", "field": "所属公司", "flex": 1},
{"headerName": "备注", "field": "备注", "flex": 2}
]
}
}
</field>
</record>
<menuitem
id="menu_yearly_maintenance_fee_report"
name="本年累计-维修费"
parent="yuthon_property_approval.property_approval_report_menu"
action="action_yearly_maintenance_fee_report"
sequence="30"
/>
</data>
</odoo>
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="action_yearly_management_fee_report" model="ir.actions.client">
<field name="name">本年累计-管理费</field>
<field name="tag">receivable_report_client</field>
<field name="context">
{
"sideBar": true,
"report_config": {
"model": "property.accounts.receivable",
"method": "get_yearly_management_fee_report",
"columns": [
{"headerName": "序号", "field": "序号", "flex": 0.6},
{"headerName": "编码", "field": "编码", "flex": 1},
{"headerName": "支出日期", "field": "支出日期", "flex": 1.2},
{"headerName": "物业名称", "field": "物业名称", "flex": 2},
{"headerName": "管理员", "field": "管理员", "flex": 1},
{"headerName": "金额", "field": "金额", "flex": 1, "type": "number"},
{"headerName": "所属公司", "field": "所属公司", "flex": 1},
{"headerName": "备注", "field": "备注", "flex": 2}
]
}
}
</field>
</record>
<menuitem
id="menu_yearly_management_fee_report"
name="本年累计-管理费"
parent="yuthon_property_approval.property_approval_report_menu"
action="action_yearly_management_fee_report"
sequence="26"
/>
</data>
</odoo>
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="action_yearly_other_fee_report" model="ir.actions.client">
<field name="name">本年累计-其他费用</field>
<field name="tag">receivable_report_client</field>
<field name="context">
{
"sideBar": true,
"report_config": {
"model": "property.accounts.receivable",
"method": "get_yearly_other_fee_report",
"columns": [
{"headerName": "序号", "field": "序号", "flex": 0.6},
{"headerName": "编码", "field": "编码", "flex": 1},
{"headerName": "支出日期", "field": "支出日期", "flex": 1.2},
{"headerName": "物业名称", "field": "物业名称", "flex": 2},
{"headerName": "管理员", "field": "管理员", "flex": 1},
{"headerName": "金额", "field": "金额", "flex": 1, "type": "number"},
{"headerName": "所属公司", "field": "所属公司", "flex": 1},
{"headerName": "备注", "field": "备注", "flex": 2}
]
}
}
</field>
</record>
<menuitem
id="menu_yearly_other_fee_report"
name="本年累计-其他费用"
parent="yuthon_property_approval.property_approval_report_menu"
action="action_yearly_other_fee_report"
sequence="29"
/>
</data>
</odoo>
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="action_yearly_penalty_report" model="ir.actions.client">
<field name="name">违约金本年累计</field>
<field name="tag">receivable_report_client</field>
<field name="context">
{
"sideBar": true,
"report_config": {
"model": "property.accounts.receivable",
"method": "get_yearly_penalty_report",
"columns": [
{"headerName": "序号", "field": "序号", "flex": 0.6},
{"headerName": "合同编号", "field": "合同编号", "flex": 1.2},
{"headerName": "物业名称", "field": "物业名称", "flex": 2},
{"headerName": "承租方名称", "field": "承租方名称", "flex": 1.5},
{"headerName": "金额", "field": "金额", "flex": 1, "type": "number"},
{"headerName": "所属公司", "field": "所属公司", "flex": 1},
{"headerName": "管理员", "field": "管理员", "flex": 1}
]
}
}
</field>
</record>
<menuitem
id="menu_yearly_penalty_report"
name="违约金本年累计"
parent="yuthon_property_approval.property_approval_report_menu"
action="action_yearly_penalty_report"
sequence="23"
/>
</data>
</odoo>
@@ -35,6 +35,11 @@ class SealsTypeSetting(models.Model):
is_effect = fields.Boolean(string="是否生效", compute="_compute_is_effect")
scope_setting_ids = fields.Many2many('seals.scope.setting', string="用章范围")
def _compute_display_name(self):
for record in self:
scope_name = record.scope_setting_id.name if record.scope_setting_id else "无"
record.display_name = f"{scope_name}{record.name}"
@api.depends('start_date', 'end_date')
def _compute_is_effect(self):
@@ -60,8 +60,7 @@
<!-- <field name="document_ids" widget="preview_many2many" string="附件清单"/>-->
</group>
<group>
<field name="scope_chapter_ids" widget="many2many_tags" options="{'no_create': True, 'no_open': True}}"/>
<field name="type_stamp_ids" widget="many2many_tags" domain="[('scope_setting_ids', 'in', scope_chapter_ids)]" options="{'no_create': True, 'no_open': True}}"/>
<field name="type_stamp_ids" widget="many2many_tags" options="{'no_create': True, 'no_open': True}}"/>
<field name="department_id" invisible="1"/>
</group>
</group>
@@ -43,8 +43,7 @@
<field name="work_end" invisible="1"/>
<field name="name"/>
<field name="users_id" options="{'no_create': True}"/>
<field name="scope_chapter_ids" widget="many2many_tags" options="{'no_create': True, 'no_open': True}}"/>
<field name="type_stamp_ids" widget="many2many_tags" domain="[('scope_setting_ids', 'in', scope_chapter_ids)]" options="{'no_create': True, 'no_open': True}}"/>
<field name="type_stamp_ids" widget="many2many_tags" options="{'no_create': True, 'no_open': True}}"/>
<field name="approval_unit"/>
<field name="remake"/>
<field name="document_ids" widget="preview_many2many"/>