From d27930ceede558bfd9a18e1b96fa8f767fd305e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?= Date: Fri, 23 Jan 2026 19:52:17 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8A=A5=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../views/yuthon_file_processing_views.xml | 3 +- .../views/yuthon_official_views.xml | 3 +- .../views/property_lease_contract.xml | 3 +- .../models/cumulative_arrears_report.py | 88 ++++++++++++ .../models/current_month_received_report.py | 92 +++++++++++++ .../current_receivable_property_fee_report.py | 112 +++++++++++++++ yuthon_report/models/deposit_total_report.py | 80 +++++++++++ .../models/management_area_report.py | 88 ++++++++++++ .../models/monthly_assessment_fee_report.py | 111 +++++++++++++++ .../models/monthly_expense_total_report.py | 98 +++++++++++++ .../models/monthly_maintenance_fee_report.py | 111 +++++++++++++++ .../models/monthly_management_fee_report.py | 112 +++++++++++++++ .../models/monthly_other_expense_report.py | 111 +++++++++++++++ .../parking_fee_current_month_receivable.py | 99 ++++++++++++++ .../parking_fee_current_month_report.py | 105 ++++++++++++++ .../models/parking_fee_yearly_report.py | 110 +++++++++++++++ yuthon_report/models/penalty_detail_report.py | 129 ++++++++++++++++++ .../models/receivable_detail_report.py | 87 ++++++++++++ yuthon_report/models/rentable_area_report.py | 73 ++++++++++ yuthon_report/models/rented_area_report.py | 86 ++++++++++++ .../models/yearly_assessment_fee_report.py | 107 +++++++++++++++ yuthon_report/models/yearly_expense_report.py | 88 ++++++++++++ .../models/yearly_maintenance_fee_report.py | 107 +++++++++++++++ .../models/yearly_management_fee_report.py | 109 +++++++++++++++ .../models/yearly_other_fee_report.py | 107 +++++++++++++++ yuthon_report/models/yearly_penalty_report.py | 91 ++++++++++++ .../views/cumulative_arrears_report.xml | 39 ++++++ .../views/current_month_received_report.xml | 36 +++++ ...current_receivable_property_fee_report.xml | 39 ++++++ yuthon_report/views/deposit_total_report.xml | 36 +++++ .../views/management_area_report.xml | 37 +++++ .../views/monthly_assessment_fee_report.xml | 37 +++++ .../views/monthly_expense_total_report.xml | 37 +++++ .../views/monthly_maintenance_fee_report.xml | 37 +++++ .../views/monthly_management_fee_report.xml | 37 +++++ .../views/monthly_other_expense_report.xml | 37 +++++ .../parking_fee_current_month_receivable.xml | 36 +++++ .../parking_fee_current_month_report.xml | 36 +++++ .../views/parking_fee_yearly_report.xml | 36 +++++ yuthon_report/views/penalty_detail_report.xml | 45 ++++++ .../views/receivable_detail_report.xml | 36 +++++ yuthon_report/views/rentable_area_report.xml | 37 +++++ yuthon_report/views/rented_area_report.xml | 38 ++++++ .../views/yearly_assessment_fee_report.xml | 37 +++++ yuthon_report/views/yearly_expense_report.xml | 36 +++++ .../views/yearly_maintenance_fee_report.xml | 37 +++++ .../views/yearly_management_fee_report.xml | 37 +++++ .../views/yearly_other_fee_report.xml | 37 +++++ yuthon_report/views/yearly_penalty_report.xml | 36 +++++ yuthon_trade/models/yuthon_trade_setting.py | 5 + .../views/yuthon_business_approval_viwes.xml | 3 +- .../views/yuthon_seals_approval_views.xml | 3 +- 52 files changed, 3167 insertions(+), 10 deletions(-) create mode 100644 yuthon_report/models/cumulative_arrears_report.py create mode 100644 yuthon_report/models/current_month_received_report.py create mode 100644 yuthon_report/models/current_receivable_property_fee_report.py create mode 100644 yuthon_report/models/deposit_total_report.py create mode 100644 yuthon_report/models/management_area_report.py create mode 100644 yuthon_report/models/monthly_assessment_fee_report.py create mode 100644 yuthon_report/models/monthly_expense_total_report.py create mode 100644 yuthon_report/models/monthly_maintenance_fee_report.py create mode 100644 yuthon_report/models/monthly_management_fee_report.py create mode 100644 yuthon_report/models/monthly_other_expense_report.py create mode 100644 yuthon_report/models/parking_fee_current_month_receivable.py create mode 100644 yuthon_report/models/parking_fee_current_month_report.py create mode 100644 yuthon_report/models/parking_fee_yearly_report.py create mode 100644 yuthon_report/models/penalty_detail_report.py create mode 100644 yuthon_report/models/receivable_detail_report.py create mode 100644 yuthon_report/models/rentable_area_report.py create mode 100644 yuthon_report/models/rented_area_report.py create mode 100644 yuthon_report/models/yearly_assessment_fee_report.py create mode 100644 yuthon_report/models/yearly_expense_report.py create mode 100644 yuthon_report/models/yearly_maintenance_fee_report.py create mode 100644 yuthon_report/models/yearly_management_fee_report.py create mode 100644 yuthon_report/models/yearly_other_fee_report.py create mode 100644 yuthon_report/models/yearly_penalty_report.py create mode 100644 yuthon_report/views/cumulative_arrears_report.xml create mode 100644 yuthon_report/views/current_month_received_report.xml create mode 100644 yuthon_report/views/current_receivable_property_fee_report.xml create mode 100644 yuthon_report/views/deposit_total_report.xml create mode 100644 yuthon_report/views/management_area_report.xml create mode 100644 yuthon_report/views/monthly_assessment_fee_report.xml create mode 100644 yuthon_report/views/monthly_expense_total_report.xml create mode 100644 yuthon_report/views/monthly_maintenance_fee_report.xml create mode 100644 yuthon_report/views/monthly_management_fee_report.xml create mode 100644 yuthon_report/views/monthly_other_expense_report.xml create mode 100644 yuthon_report/views/parking_fee_current_month_receivable.xml create mode 100644 yuthon_report/views/parking_fee_current_month_report.xml create mode 100644 yuthon_report/views/parking_fee_yearly_report.xml create mode 100644 yuthon_report/views/penalty_detail_report.xml create mode 100644 yuthon_report/views/receivable_detail_report.xml create mode 100644 yuthon_report/views/rentable_area_report.xml create mode 100644 yuthon_report/views/rented_area_report.xml create mode 100644 yuthon_report/views/yearly_assessment_fee_report.xml create mode 100644 yuthon_report/views/yearly_expense_report.xml create mode 100644 yuthon_report/views/yearly_maintenance_fee_report.xml create mode 100644 yuthon_report/views/yearly_management_fee_report.xml create mode 100644 yuthon_report/views/yearly_other_fee_report.xml create mode 100644 yuthon_report/views/yearly_penalty_report.xml diff --git a/yuthon_official/views/yuthon_file_processing_views.xml b/yuthon_official/views/yuthon_file_processing_views.xml index bf86c2658..21208adef 100644 --- a/yuthon_official/views/yuthon_file_processing_views.xml +++ b/yuthon_official/views/yuthon_file_processing_views.xml @@ -88,8 +88,7 @@ - - + diff --git a/yuthon_official/views/yuthon_official_views.xml b/yuthon_official/views/yuthon_official_views.xml index 035b5d3eb..b325ddc74 100644 --- a/yuthon_official/views/yuthon_official_views.xml +++ b/yuthon_official/views/yuthon_official_views.xml @@ -96,8 +96,7 @@ - - + diff --git a/yuthon_property_approval/views/property_lease_contract.xml b/yuthon_property_approval/views/property_lease_contract.xml index 287263b80..b5c410f01 100644 --- a/yuthon_property_approval/views/property_lease_contract.xml +++ b/yuthon_property_approval/views/property_lease_contract.xml @@ -96,8 +96,7 @@ - - + diff --git a/yuthon_report/models/cumulative_arrears_report.py b/yuthon_report/models/cumulative_arrears_report.py new file mode 100644 index 000000000..94b7e7715 --- /dev/null +++ b/yuthon_report/models/cumulative_arrears_report.py @@ -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, + } + } + diff --git a/yuthon_report/models/current_month_received_report.py b/yuthon_report/models/current_month_received_report.py new file mode 100644 index 000000000..8ebc14710 --- /dev/null +++ b/yuthon_report/models/current_month_received_report.py @@ -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, + } + } + diff --git a/yuthon_report/models/current_receivable_property_fee_report.py b/yuthon_report/models/current_receivable_property_fee_report.py new file mode 100644 index 000000000..374fcd0b8 --- /dev/null +++ b/yuthon_report/models/current_receivable_property_fee_report.py @@ -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, + } + } + diff --git a/yuthon_report/models/deposit_total_report.py b/yuthon_report/models/deposit_total_report.py new file mode 100644 index 000000000..dd16335bc --- /dev/null +++ b/yuthon_report/models/deposit_total_report.py @@ -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, + } + } + diff --git a/yuthon_report/models/management_area_report.py b/yuthon_report/models/management_area_report.py new file mode 100644 index 000000000..eb43bf9b0 --- /dev/null +++ b/yuthon_report/models/management_area_report.py @@ -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, + } + } + diff --git a/yuthon_report/models/monthly_assessment_fee_report.py b/yuthon_report/models/monthly_assessment_fee_report.py new file mode 100644 index 000000000..ec4214bf1 --- /dev/null +++ b/yuthon_report/models/monthly_assessment_fee_report.py @@ -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, + } + } + diff --git a/yuthon_report/models/monthly_expense_total_report.py b/yuthon_report/models/monthly_expense_total_report.py new file mode 100644 index 000000000..759d60078 --- /dev/null +++ b/yuthon_report/models/monthly_expense_total_report.py @@ -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, + } + } + diff --git a/yuthon_report/models/monthly_maintenance_fee_report.py b/yuthon_report/models/monthly_maintenance_fee_report.py new file mode 100644 index 000000000..f31fdf03f --- /dev/null +++ b/yuthon_report/models/monthly_maintenance_fee_report.py @@ -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, + } + } + diff --git a/yuthon_report/models/monthly_management_fee_report.py b/yuthon_report/models/monthly_management_fee_report.py new file mode 100644 index 000000000..5a1848d99 --- /dev/null +++ b/yuthon_report/models/monthly_management_fee_report.py @@ -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, + } + } + diff --git a/yuthon_report/models/monthly_other_expense_report.py b/yuthon_report/models/monthly_other_expense_report.py new file mode 100644 index 000000000..8ff7cdd58 --- /dev/null +++ b/yuthon_report/models/monthly_other_expense_report.py @@ -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, + } + } + diff --git a/yuthon_report/models/parking_fee_current_month_receivable.py b/yuthon_report/models/parking_fee_current_month_receivable.py new file mode 100644 index 000000000..10b931500 --- /dev/null +++ b/yuthon_report/models/parking_fee_current_month_receivable.py @@ -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, + } + } + diff --git a/yuthon_report/models/parking_fee_current_month_report.py b/yuthon_report/models/parking_fee_current_month_report.py new file mode 100644 index 000000000..ae485a17b --- /dev/null +++ b/yuthon_report/models/parking_fee_current_month_report.py @@ -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, + } + } + diff --git a/yuthon_report/models/parking_fee_yearly_report.py b/yuthon_report/models/parking_fee_yearly_report.py new file mode 100644 index 000000000..538bb8940 --- /dev/null +++ b/yuthon_report/models/parking_fee_yearly_report.py @@ -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, + } + } + diff --git a/yuthon_report/models/penalty_detail_report.py b/yuthon_report/models/penalty_detail_report.py new file mode 100644 index 000000000..442228643 --- /dev/null +++ b/yuthon_report/models/penalty_detail_report.py @@ -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, + } + } + diff --git a/yuthon_report/models/receivable_detail_report.py b/yuthon_report/models/receivable_detail_report.py new file mode 100644 index 000000000..0548c217a --- /dev/null +++ b/yuthon_report/models/receivable_detail_report.py @@ -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, + } + } + diff --git a/yuthon_report/models/rentable_area_report.py b/yuthon_report/models/rentable_area_report.py new file mode 100644 index 000000000..df6f0b54c --- /dev/null +++ b/yuthon_report/models/rentable_area_report.py @@ -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, + } + } + diff --git a/yuthon_report/models/rented_area_report.py b/yuthon_report/models/rented_area_report.py new file mode 100644 index 000000000..df845157a --- /dev/null +++ b/yuthon_report/models/rented_area_report.py @@ -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, + } + } + diff --git a/yuthon_report/models/yearly_assessment_fee_report.py b/yuthon_report/models/yearly_assessment_fee_report.py new file mode 100644 index 000000000..24864f09f --- /dev/null +++ b/yuthon_report/models/yearly_assessment_fee_report.py @@ -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, + } + } + diff --git a/yuthon_report/models/yearly_expense_report.py b/yuthon_report/models/yearly_expense_report.py new file mode 100644 index 000000000..b3200f03f --- /dev/null +++ b/yuthon_report/models/yearly_expense_report.py @@ -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, + } + } + diff --git a/yuthon_report/models/yearly_maintenance_fee_report.py b/yuthon_report/models/yearly_maintenance_fee_report.py new file mode 100644 index 000000000..55d89adeb --- /dev/null +++ b/yuthon_report/models/yearly_maintenance_fee_report.py @@ -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, + } + } + diff --git a/yuthon_report/models/yearly_management_fee_report.py b/yuthon_report/models/yearly_management_fee_report.py new file mode 100644 index 000000000..26a4ce2e9 --- /dev/null +++ b/yuthon_report/models/yearly_management_fee_report.py @@ -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, + } + } + diff --git a/yuthon_report/models/yearly_other_fee_report.py b/yuthon_report/models/yearly_other_fee_report.py new file mode 100644 index 000000000..33fa1b490 --- /dev/null +++ b/yuthon_report/models/yearly_other_fee_report.py @@ -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, + } + } + diff --git a/yuthon_report/models/yearly_penalty_report.py b/yuthon_report/models/yearly_penalty_report.py new file mode 100644 index 000000000..2de089c83 --- /dev/null +++ b/yuthon_report/models/yearly_penalty_report.py @@ -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, + } + } + diff --git a/yuthon_report/views/cumulative_arrears_report.xml b/yuthon_report/views/cumulative_arrears_report.xml new file mode 100644 index 000000000..304958079 --- /dev/null +++ b/yuthon_report/views/cumulative_arrears_report.xml @@ -0,0 +1,39 @@ + + + + + 累计欠收 + receivable_report_client + + { + "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} + ] + } + } + + + + + + + diff --git a/yuthon_report/views/current_month_received_report.xml b/yuthon_report/views/current_month_received_report.xml new file mode 100644 index 000000000..935fa12af --- /dev/null +++ b/yuthon_report/views/current_month_received_report.xml @@ -0,0 +1,36 @@ + + + + + 归属本月实收 + receivable_report_client + + { + "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} + ] + } + } + + + + + + + diff --git a/yuthon_report/views/current_receivable_property_fee_report.xml b/yuthon_report/views/current_receivable_property_fee_report.xml new file mode 100644 index 000000000..d021cc685 --- /dev/null +++ b/yuthon_report/views/current_receivable_property_fee_report.xml @@ -0,0 +1,39 @@ + + + + + 当期应收-物管水电费 + receivable_report_client + + { + "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} + ] + } + } + + + + + + + diff --git a/yuthon_report/views/deposit_total_report.xml b/yuthon_report/views/deposit_total_report.xml new file mode 100644 index 000000000..00b8bf3e5 --- /dev/null +++ b/yuthon_report/views/deposit_total_report.xml @@ -0,0 +1,36 @@ + + + + + 押金累计总额 + receivable_report_client + + { + "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} + ] + } + } + + + + + + + diff --git a/yuthon_report/views/management_area_report.xml b/yuthon_report/views/management_area_report.xml new file mode 100644 index 000000000..72d2a05e7 --- /dev/null +++ b/yuthon_report/views/management_area_report.xml @@ -0,0 +1,37 @@ + + + + + 管理面积报表 + receivable_report_client + + { + "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} + ] + } + } + + + + + + + diff --git a/yuthon_report/views/monthly_assessment_fee_report.xml b/yuthon_report/views/monthly_assessment_fee_report.xml new file mode 100644 index 000000000..7171644a5 --- /dev/null +++ b/yuthon_report/views/monthly_assessment_fee_report.xml @@ -0,0 +1,37 @@ + + + + + 本月支出-评估费 + receivable_report_client + + { + "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} + ] + } + } + + + + + + + diff --git a/yuthon_report/views/monthly_expense_total_report.xml b/yuthon_report/views/monthly_expense_total_report.xml new file mode 100644 index 000000000..c7b5fb689 --- /dev/null +++ b/yuthon_report/views/monthly_expense_total_report.xml @@ -0,0 +1,37 @@ + + + + + 本月支出-合计 + receivable_report_client + + { + "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} + ] + } + } + + + + + + + diff --git a/yuthon_report/views/monthly_maintenance_fee_report.xml b/yuthon_report/views/monthly_maintenance_fee_report.xml new file mode 100644 index 000000000..d2a269e63 --- /dev/null +++ b/yuthon_report/views/monthly_maintenance_fee_report.xml @@ -0,0 +1,37 @@ + + + + + 本月支出-维修费 + receivable_report_client + + { + "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} + ] + } + } + + + + + + + diff --git a/yuthon_report/views/monthly_management_fee_report.xml b/yuthon_report/views/monthly_management_fee_report.xml new file mode 100644 index 000000000..b83d07f7a --- /dev/null +++ b/yuthon_report/views/monthly_management_fee_report.xml @@ -0,0 +1,37 @@ + + + + + 本月支出-管理费 + receivable_report_client + + { + "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} + ] + } + } + + + + + + + diff --git a/yuthon_report/views/monthly_other_expense_report.xml b/yuthon_report/views/monthly_other_expense_report.xml new file mode 100644 index 000000000..eadf7ba7a --- /dev/null +++ b/yuthon_report/views/monthly_other_expense_report.xml @@ -0,0 +1,37 @@ + + + + + 本月支出-其他 + receivable_report_client + + { + "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} + ] + } + } + + + + + + + diff --git a/yuthon_report/views/parking_fee_current_month_receivable.xml b/yuthon_report/views/parking_fee_current_month_receivable.xml new file mode 100644 index 000000000..8333afc7c --- /dev/null +++ b/yuthon_report/views/parking_fee_current_month_receivable.xml @@ -0,0 +1,36 @@ + + + + + 停车费本月应收 + receivable_report_client + + { + "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} + ] + } + } + + + + + + + diff --git a/yuthon_report/views/parking_fee_current_month_report.xml b/yuthon_report/views/parking_fee_current_month_report.xml new file mode 100644 index 000000000..4aef2d783 --- /dev/null +++ b/yuthon_report/views/parking_fee_current_month_report.xml @@ -0,0 +1,36 @@ + + + + + 停车费本月实收 + receivable_report_client + + { + "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} + ] + } + } + + + + + + + diff --git a/yuthon_report/views/parking_fee_yearly_report.xml b/yuthon_report/views/parking_fee_yearly_report.xml new file mode 100644 index 000000000..8b2f5f002 --- /dev/null +++ b/yuthon_report/views/parking_fee_yearly_report.xml @@ -0,0 +1,36 @@ + + + + + 停车费本年累计 + receivable_report_client + + { + "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} + ] + } + } + + + + + + + diff --git a/yuthon_report/views/penalty_detail_report.xml b/yuthon_report/views/penalty_detail_report.xml new file mode 100644 index 000000000..af426a5fa --- /dev/null +++ b/yuthon_report/views/penalty_detail_report.xml @@ -0,0 +1,45 @@ + + + + + 违约金明细报表 + receivable_report_client + + { + "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} + ] + } + } + + + + + + + diff --git a/yuthon_report/views/receivable_detail_report.xml b/yuthon_report/views/receivable_detail_report.xml new file mode 100644 index 000000000..e49b204be --- /dev/null +++ b/yuthon_report/views/receivable_detail_report.xml @@ -0,0 +1,36 @@ + + + + + 合同应收 + receivable_report_client + + { + "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} + ] + } + } + + + + + + + diff --git a/yuthon_report/views/rentable_area_report.xml b/yuthon_report/views/rentable_area_report.xml new file mode 100644 index 000000000..98d68978b --- /dev/null +++ b/yuthon_report/views/rentable_area_report.xml @@ -0,0 +1,37 @@ + + + + + 可租面积 + receivable_report_client + + { + "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} + ] + } + } + + + + + + + diff --git a/yuthon_report/views/rented_area_report.xml b/yuthon_report/views/rented_area_report.xml new file mode 100644 index 000000000..9baedbf24 --- /dev/null +++ b/yuthon_report/views/rented_area_report.xml @@ -0,0 +1,38 @@ + + + + + 已租面积 + receivable_report_client + + { + "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} + ] + } + } + + + + + + + diff --git a/yuthon_report/views/yearly_assessment_fee_report.xml b/yuthon_report/views/yearly_assessment_fee_report.xml new file mode 100644 index 000000000..a12be209c --- /dev/null +++ b/yuthon_report/views/yearly_assessment_fee_report.xml @@ -0,0 +1,37 @@ + + + + + 本年累计-评估费用 + receivable_report_client + + { + "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} + ] + } + } + + + + + + + diff --git a/yuthon_report/views/yearly_expense_report.xml b/yuthon_report/views/yearly_expense_report.xml new file mode 100644 index 000000000..028650988 --- /dev/null +++ b/yuthon_report/views/yearly_expense_report.xml @@ -0,0 +1,36 @@ + + + + + 支出本年-合计 + receivable_report_client + + { + "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} + ] + } + } + + + + + + + diff --git a/yuthon_report/views/yearly_maintenance_fee_report.xml b/yuthon_report/views/yearly_maintenance_fee_report.xml new file mode 100644 index 000000000..3f20f5424 --- /dev/null +++ b/yuthon_report/views/yearly_maintenance_fee_report.xml @@ -0,0 +1,37 @@ + + + + + 本年累计-维修费 + receivable_report_client + + { + "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} + ] + } + } + + + + + + + diff --git a/yuthon_report/views/yearly_management_fee_report.xml b/yuthon_report/views/yearly_management_fee_report.xml new file mode 100644 index 000000000..742217a8a --- /dev/null +++ b/yuthon_report/views/yearly_management_fee_report.xml @@ -0,0 +1,37 @@ + + + + + 本年累计-管理费 + receivable_report_client + + { + "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} + ] + } + } + + + + + + + diff --git a/yuthon_report/views/yearly_other_fee_report.xml b/yuthon_report/views/yearly_other_fee_report.xml new file mode 100644 index 000000000..a2565169e --- /dev/null +++ b/yuthon_report/views/yearly_other_fee_report.xml @@ -0,0 +1,37 @@ + + + + + 本年累计-其他费用 + receivable_report_client + + { + "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} + ] + } + } + + + + + + + diff --git a/yuthon_report/views/yearly_penalty_report.xml b/yuthon_report/views/yearly_penalty_report.xml new file mode 100644 index 000000000..e38a9dfaf --- /dev/null +++ b/yuthon_report/views/yearly_penalty_report.xml @@ -0,0 +1,36 @@ + + + + + 违约金本年累计 + receivable_report_client + + { + "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} + ] + } + } + + + + + + + diff --git a/yuthon_trade/models/yuthon_trade_setting.py b/yuthon_trade/models/yuthon_trade_setting.py index 5e5fb712f..c274a1af7 100644 --- a/yuthon_trade/models/yuthon_trade_setting.py +++ b/yuthon_trade/models/yuthon_trade_setting.py @@ -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): diff --git a/yuthon_trade/views/yuthon_business_approval_viwes.xml b/yuthon_trade/views/yuthon_business_approval_viwes.xml index 40cba2aa4..f91a6bc3c 100644 --- a/yuthon_trade/views/yuthon_business_approval_viwes.xml +++ b/yuthon_trade/views/yuthon_business_approval_viwes.xml @@ -60,8 +60,7 @@ - - + diff --git a/yuthon_trade/views/yuthon_seals_approval_views.xml b/yuthon_trade/views/yuthon_seals_approval_views.xml index 85ddeac8a..62cb54c01 100644 --- a/yuthon_trade/views/yuthon_seals_approval_views.xml +++ b/yuthon_trade/views/yuthon_seals_approval_views.xml @@ -43,8 +43,7 @@ - - +