# -*- 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='押金') lightbox_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', '押金'), ('lightbox_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': { 'order': 'management_type_id desc', }, 'target': 'current', }