From 364efe6c276860786b949cb137553d352f0a816d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?= Date: Mon, 23 Mar 2026 10:30:22 +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 --- .../models/receivable_flow_report.py | 76 +++++++------------ 1 file changed, 28 insertions(+), 48 deletions(-) diff --git a/yuthon_report/models/receivable_flow_report.py b/yuthon_report/models/receivable_flow_report.py index baf5d4304..c17ffb18b 100644 --- a/yuthon_report/models/receivable_flow_report.py +++ b/yuthon_report/models/receivable_flow_report.py @@ -1,4 +1,5 @@ import datetime +import re from odoo import api, models @@ -75,8 +76,9 @@ class ReceivableFlowReport(models.Model): wizard_map = {} # {receivable_id: [wizards]} for wz in all_wizards: wizard_map.setdefault(wz.receivable_id.id, []).append(wz) - # 预取所有 pay_line_ids + # 预取所有 pay_line_ids 和 penalty_line_ids all_pay_lines = all_wizards.mapped('pay_line_ids') + all_wizards.mapped('penalty_line_ids') # 预加载违约金计算明细 # 按 wizard_id 分组 pay_line_map = {} # {wizard_id: [pay_lines]} for pl in all_pay_lines: @@ -108,54 +110,32 @@ class ReceivableFlowReport(models.Model): if rec.submitted == 0: charge_status = "已收" - # 计算违约金相关信息 - # 计算欠租金额(直接使用合同的rent字段,即月租金) - rent_owed = 0.0 - if rec.contract_id: - rent_owed = rec.contract_id.rent or 0.0 - rent_owed = rent_owed or rec.rent_owed or 0.0 - - # 计算每天违约金(欠租金额 × 违约金比例) - per_day = 0.0 - if rent_owed > 0 and rec.contract_id and rec.contract_id.percentage_liquidated_damages: - penalty_ratio = rec.contract_id.percentage_liquidated_damages.name or "0" - try: - penalty_ratio = float(penalty_ratio) - per_day = rent_owed * penalty_ratio - except ValueError: - per_day = 0.0 - per_day = per_day or rec.difference_amount or rec.per_day or 0.0 - - # 计算违约金金额(每天违约金 × 违约天数) - penalty_amount = 0.0 - billing_period = rec.charging_range or "" - days_diff = 0 - - if billing_period: - try: - # 兼容中文“到”和英文“~”两种分隔符 - if ' 到 ' in billing_period: - date_parts = billing_period.split(' 到 ') - elif ' ~ ' in billing_period: - date_parts = billing_period.split(' ~ ') - else: - date_parts = [] - if len(date_parts) == 2: - start_date_str = date_parts[0].strip() - end_date_str = date_parts[1].strip() - start_date = datetime.datetime.strptime(start_date_str, "%Y-%m-%d").date() - end_date = datetime.datetime.strptime(end_date_str, "%Y-%m-%d").date() - days_diff = (end_date - start_date).days + 1 - billing_period = f"{days_diff} {start_date_str} ~ {end_date_str}" - except Exception: - billing_period = rec.charging_range or "" - days_diff = 0 - - # 计算违约金金额 - penalty_amount = round(days_diff * per_day, 2) - - # 获取违约金收款信息(从预加载字典取) + # 从 penalty.line.wizard 取违约金信息(唯一数据源) wz_list = wizard_map.get(rec.id, []) + + # 违约金字段:有 wizard 才显示,否则全为空/0 + billing_period = "" + days_diff = 0 + penalty_amount = 0.0 + if wz_list: + wz = wz_list[0] + days_diff = wz.days_overdue or 0 + penalty_amount = round(wz.total_penalty or 0.0, 2) + # 计算时间:取 penalty > 0 的明细行,从每行的 remake 解析起止日期 + penalty_lines = [pl for pl in wz.penalty_line_ids if pl.penalty > 0] + if penalty_lines: + # remake 格式: "违约金:2026-02-22至2026-03-23" + # 每行 remake 都包含起止日期,收集所有行的日期取最早和最晚 + all_dates = [] + for pl in penalty_lines: + dates_in_remake = re.findall(r'(\d{4}-\d{2}-\d{2})', pl.remake or '') + for d in dates_in_remake: + try: + all_dates.append(datetime.datetime.strptime(d, '%Y-%m-%d').date()) + except ValueError: + pass + if all_dates: + billing_period = f"{min(all_dates).strftime('%Y-%m-%d')} ~ {max(all_dates).strftime('%Y-%m-%d')}" penalty_pay_lines = [] for wz in wz_list: penalty_pay_lines.extend(pay_line_map.get(wz.id, []))