import datetime import calendar from odoo import api, models from .report_utils import _id_list class TotalReceivedReport(models.Model): _inherit = 'receivable.modify.line' @api.model def get_total_received_report(self, params): """本月实收总额 = 归属本月实收 + 本月预收 取数逻辑:收款日期(reconciliation_date)在查询月内的所有收款记录,金额取 amount """ summary_only = params.get("summary_only", False) domain = [ ('reconciliation_date', '!=', False), ('receivable_id.is_invalid', '!=', True), ] # 收款日期在查询月内 if params.get('date_start'): query_date = datetime.datetime.fromisoformat(params['date_start']).date() month_start = query_date.replace(day=1) last_day = calendar.monthrange(query_date.year, query_date.month)[1] month_end = query_date.replace(day=last_day) domain += [ ('reconciliation_date', '>=', month_start), ('reconciliation_date', '<=', month_end), ] if params.get('keyword'): keyword = params['keyword'] domain += ['|', '|', ('receivable_id.lessee_id.name', 'ilike', keyword), ('receivable_id.property_name', 'ilike', keyword), ('receivable_id.contract_id.contract_code', 'ilike', keyword), ] condition = params.get('condition') # 多选标准化:partner_id 统一为列表,配合 'in' 操作符 partner_id = _id_list(params.get('partner_id')) company_id = _id_list(params.get('company_id')) management_type_id = params.get('management_type_id') property_id = params.get('property_id') lessee_id = params.get('lessee_id') contract_id = params.get('contract_id') if condition: domain.append(('receivable_id.tenure_type', '=', condition)) # 片区管理员:多选,用 'in' 操作符 if partner_id: domain.append(('receivable_id.precinct_id', 'in', partner_id)) if company_id: domain.append(('receivable_id.company_id', 'in', company_id)) # 经营收入只统计租金类,排除押金、违约金 exclude_types = ['押金', '违约金'] if management_type_id: domain.append(('receivable_id.management_type_id', '=', management_type_id)) else: domain.append(('receivable_id.management_type_id.name', 'not in', exclude_types)) if property_id: domain.append(('receivable_id.property_ids', 'in', [property_id])) if lessee_id: domain.append(('receivable_id.lessee_id', '=', lessee_id)) if contract_id: domain.append(('receivable_id.contract_id', '=', contract_id)) lines = self.search(domain, order='reconciliation_date asc') if summary_only: total_amount = sum(round(line.amount or 0.0, 2) for line in lines) stats = [{'name': '合计', 'amount': round(total_amount, 2)}] return {'rows': [], 'stats': stats} rows = [] total_amount = 0.0 for idx, line in enumerate(lines, start=1): rec = line.receivable_id contract = rec.contract_id if rec else None amount = round(line.amount or 0.0, 2) total_amount += amount # 物业穿透 property_val = '' if contract and contract.lease_ids and contract.lease_ids[0].address_id: prop = contract.lease_ids[0].address_id property_val = { 'model': 'yuthon.property', 'id': prop.id, 'display_name': rec.property_name or prop.name, } elif rec: property_val = rec.property_name or '' # 合同穿透 contract_val = '' if contract: contract_val = { 'model': 'property.lease.contract', 'id': contract.id, 'display_name': contract.contract_code or '', } # 承租方穿透 lessee_val = '' if rec and rec.lessee_id: lessee_val = { 'model': 'property.tenant.information', 'id': rec.lessee_id.id, 'display_name': rec.lessee_id.name, } # 应收金额 = original_amount - discount_amount receivable_amount = round((rec.original_amount or 0.0) - (rec.discount_amount or 0.0), 2) rows.append({ '序号': idx, '物业名称': property_val, '合同编号': contract_val, '承租方名称': lessee_val, '收款类型': rec.management_type_id.name if rec and rec.management_type_id else '', '应收开始': rec.due_date.strftime('%Y-%m-%d') if rec and rec.due_date else '', '应收结束': rec.due_date_end.strftime('%Y-%m-%d') if rec and rec.due_date_end else '', '金额': amount, '应收金额': receivable_amount, '所属公司': contract.lessor_id.name if contract and contract.lessor_id else '', '管理员': { 'model': 'res.users', 'id': rec.admin_id.id, 'display_name': rec.admin_id.name, } if rec.admin_id else '', }) stats = [{'name': '合计', 'amount': round(total_amount, 2)}] return {'rows': rows, 'stats': stats}