From 0cc5ea09dbec8e95c3fce86ed436cb4dcf498b17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?= <9664676+pengyuthon@user.noreply.gitee.com> Date: Sun, 20 Sep 2026 20:33:53 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../property_accounts_receivable_summary.py | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 yuthon_property_approval/models/property_accounts_receivable_summary.py diff --git a/yuthon_property_approval/models/property_accounts_receivable_summary.py b/yuthon_property_approval/models/property_accounts_receivable_summary.py new file mode 100644 index 000000000..9adc1c88e --- /dev/null +++ b/yuthon_property_approval/models/property_accounts_receivable_summary.py @@ -0,0 +1,86 @@ +# -*- coding: utf-8 -*- +from odoo import fields, models, tools + + +class PropertyAccountsReceivableSummary(models.Model): + """应收账单未收汇总(按合同)—— 一行一个合同,聚合各应收类型未收金额。 + + 采用 PostgreSQL 视图 + _auto=False 模型:视图实时按合同聚合, + 无需定时任务同步,web 列表 / searchpanel / read_group 全部走标准 ORM。 + """ + _name = 'property.accounts.receivable.summary' + _description = '应收账单未收汇总(按合同)' + _auto = False + _order = 'id desc' + + contract_id = fields.Many2one('property.lease.contract', string='合同') + property_name = fields.Text(string='物业地址') + admin_id = fields.Many2one('res.users', string='片区管理员') + + rent_unpaid = fields.Float(string='租金') + management_unpaid = fields.Float(string='管理费') + water_unpaid = fields.Float(string='水费') + electricity_unpaid = fields.Float(string='电费') + penalty_unpaid = fields.Float(string='违约金') + deposit_unpaid = fields.Float(string='押金') + ad_unpaid = fields.Float(string='广告灯箱费') + other_unpaid = fields.Float(string='其他') + + # 7 个单独展示的类型;其余类型(有偿服务收入/停车费/灯箱/管子间/更名费/维修费/其他费用/ + # 评估费/保险费,以及以后新增的类型)全部归入「其他」 + _SUMMARY_TYPES = ['租金', '管理费', '水费', '电费', '违约金', '押金', '广告灯箱费'] + + def init(self): + tools.drop_view_if_exists(self.env.cr, self._table) + main_cols = "" + for fname, tname in [ + ('rent_unpaid', '租金'), ('management_unpaid', '管理费'), ('water_unpaid', '水费'), + ('electricity_unpaid', '电费'), ('penalty_unpaid', '违约金'), ('deposit_unpaid', '押金'), + ('ad_unpaid', '广告灯箱费'), + ]: + main_cols += "\n COALESCE(SUM(CASE WHEN t.name = '%s' THEN r.submitted ELSE 0.0 END), 0.0) AS %s," % (tname, fname) + excluded = ", ".join("'%s'" % t for t in self._SUMMARY_TYPES) + self.env.cr.execute(""" + CREATE OR REPLACE VIEW %(table)s AS ( + SELECT + r.contract_id AS id, + r.contract_id AS contract_id, + c.use_address AS property_name, + c.admin_id AS admin_id,%(main_cols)s + COALESCE(SUM(CASE WHEN t.name NOT IN (%(excluded)s) THEN r.submitted ELSE 0.0 END), 0.0) AS other_unpaid + FROM property_accounts_receivable r + LEFT JOIN property_lease_contract c ON c.id = r.contract_id + LEFT JOIN yuthon_management_type t ON t.id = r.management_type_id + WHERE r.contract_id IS NOT NULL + AND r.deposit_state IN ('unpaid', 'no_paid') + AND COALESCE(r.is_reconciliation, false) = false + GROUP BY r.contract_id, c.use_address, c.admin_id + HAVING COALESCE(SUM(r.submitted), 0) > 0 + ) + """ % {'table': self._table, 'main_cols': main_cols, 'excluded': excluded}) + + def action_open_unpaid_detail(self): + """未收明细:打开该合同下 未收/收部分 且 未对账 的应收账单, + 复用 view_property_accounts_receivable_tree,默认按应收类型分组""" + self.ensure_one() + tree_view = self.env.ref('yuthon_property.view_property_accounts_receivable_tree') + search_view = self.env.ref('yuthon_property_approval.view_property_accounts_receivable_search') + return { + 'name': '未收明细 - %s' % (self.contract_id.display_name or ''), + 'type': 'ir.actions.act_window', + 'res_model': 'property.accounts.receivable', + 'view_mode': 'tree', + 'view_id': tree_view.id, + # 前端要求 search_view_id 为 [id, name] 列表格式,纯 int 会被忽略而回落到默认 search 视图 + 'search_view_id': [search_view.id, search_view.name], + 'domain': [ + ('contract_id', '=', self.contract_id.id), + ('deposit_state', 'in', ['unpaid', 'no_paid']), + ('is_reconciliation', '=', False), + ], + 'context': { + 'group_by': 'management_type_id', + 'order': 'management_type_id desc', + }, + 'target': 'current', + }