利息计算-修正版.py

下载原文件

# -*- coding: utf-8 -*-
"""
利息测算脚本(修正版)
改动说明(相对原第5轮脚本):
  1. 参数不再手工抄写:本金、起息日、月利率、已还5,000元及其日期,均从同目录
     《起诉状-修正版.txt》正文自动读取并回显核对;另与《证据目录-修正版.txt》
     《待核清单-修正版.txt》做交叉一致性检查。
  2. 不代选待定口径:起诉日、利率理解(月1%/年10%)、抵充口径(抵息/抵本)、
     3,000元性质、LPR数值,文书中均为【待定/待核】——本脚本一律不替承办律师选定,
     只把各情景的算术结果分别列出,供选定后填入文书。
  3. 日期全部显式写出(ISO与中文并列);天数口径、折算口径在报告"口径与假设"逐条列明。
  4. 结果另存本目录《利息计算结果-修正版.txt》,不覆盖、不修改任何其他文件。
用法:python3 利息计算-修正版.py
依赖:仅Python标准库。
"""
import os
import re
import sys
from datetime import date
from decimal import Decimal, ROUND_HALF_UP

# ───────────────────────── 参数区 ─────────────────────────
# 起诉日:起诉状为"暂计至【起诉之日待定】",本脚本不代选。
# 承办律师确定起诉日后,改下一行(如 起诉日 = date(2026, 11, 2))并重跑,
# 报告会另给出"截至起诉日"的各情景金额,供诉请二填列时选用。
起诉日 = None

# 测算截止日:None=取运行日。它只是给算式一个展示截止点,【不是】选定的起诉日;
# 报告中所有以此日截止的金额均按"测算展示"对待。
测算截止日 = None

# 以下两项为待定口径的备用参数(非选定):
年利率备用_一分按年10 = Decimal("0.10")  # "一分"按年利率10%理解的备用口径(清单第4项,待定)
LPR备选_年利率 = Decimal("0.030")        # 沿用原第5轮脚本所列"2025年5月20日起一年期LPR 3.0%"
                                         # 【待核:以起诉日适用报价为准,本脚本不代核、不更新】
LPR备选_起算日 = date(2025, 7, 1)        # 展期届满次日(起诉状备选方案、清单第5项)
# ───────────────────────────────────────────────────────────

BASE = os.path.dirname(os.path.abspath(__file__))
诉状名 = "起诉状-修正版.txt"
证据名 = "证据目录-修正版.txt"
清单名 = "待核清单-修正版.txt"
结果名 = "利息计算结果-修正版.txt"

行 = []


def 输出(s=""):
    行.append(s)
    print(s)


def 读文本(p):
    with open(p, "r", encoding="utf-8") as f:
        return f.read()


def 紧(t):
    """去除全部空白(含全角空格),便于对折行文书做跨行匹配。"""
    return re.sub(r"\s+", "", t)


D2 = Decimal("0.01")


def 舍(x):
    return x.quantize(D2, rounding=ROUND_HALF_UP)


def 天数(起, 止):
    """算头不算尾:起息日计息、截止日不计息。"""
    return (止 - 起).days


def 计息(本金, 起, 止, 日利率):
    d = 天数(起, 止)
    return d, 舍(本金 * 日利率 * d)


def 中(日):
    return f"{日.isoformat()}({日.year}年{日.month}月{日.day}日)"


def 打明细(截止, 名义利率, 式样):
    """逐笔列算式;式样 '月'→日利率=名义利率÷30,'年'→日利率=名义利率÷365。"""
    日利率 = 名义利率 / 30 if 式样 == "月" else 名义利率 / 365
    总 = Decimal("0")
    for 序, (本金, 起, 说明) in enumerate(借款, 1):
        d, i = 计息(本金, 起, 截止, 日利率)
        总 += i
        输出(f"    第{序}笔 本金{本金:,.0f}元({说明})")
        输出(f"      起止:{中(起)} 至 {中(截止)},算头不算尾共 {d} 天")
        除数 = "30" if 式样 == "月" else "365"
        输出(f"      算式:{本金:,.0f} × {名义利率:%} ÷ {除数} × {d} = {i:,.2f} 元")
    return 总


def 截至已生息(止, 日利率):
    return sum(计息(p, d, 止, 日利率)[1] for p, d, _ in 借款)


# ════════════════════════ 主流程 ════════════════════════
缺失 = [n for n in (诉状名, 证据名, 清单名) if not os.path.exists(os.path.join(BASE, n))]
if 缺失:
    输出("【错误】以下输入文件缺失,无法从实际文书读取参数,脚本拒绝在无依据下计算:")
    for n in 缺失:
        输出(f"  - {n}")
    with open(os.path.join(BASE, 结果名), "w", encoding="utf-8") as f:
        f.write("\n".join(行) + "\n")
    sys.exit(1)

诉状原文 = 读文本(os.path.join(BASE, 诉状名))
证据原文 = 读文本(os.path.join(BASE, 证据名))
清单原文 = 读文本(os.path.join(BASE, 清单名))
诉紧, 证紧, 清紧 = 紧(诉状原文), 紧(证据原文), 紧(清单原文)

输出("利息测算报告(修正版脚本自动生成)")
输出(f"运行日期:{date.today().isoformat()}")
输出("参数来源:本目录《起诉状-修正版.txt》(计算参数);《证据目录-修正版.txt》"
     "《待核清单-修正版.txt》(交叉核对)。")
输出("原则:文书中【待定/待核】的口径本脚本不代选,只分别列出各情景算术结果。")
输出()

# ── 一、从起诉状读取参数 ──
输出("一、从起诉状读取的参数(自动匹配)")
本金项 = re.findall(r"以([\d,]+)元为基数自(\d{4})年(\d{1,2})月(\d{1,2})日起", 诉紧)
月利率匹配 = re.findall(r"按月利率([\d.]+)%计算", 诉紧)
已还匹配 = re.findall(r"已付5,000元((\d{4})年(\d{1,2})月(\d{1,2})日)", 诉紧)

解析通过 = True
if len(本金项) != 3:
    输出(f"  ✗ 诉请二本金项匹配到 {len(本金项)} 笔(应为3笔),中止计算。")
    解析通过 = False
else:
    借款 = []
    期望 = [("80,000", 2024, 3, 8), ("10,000", 2024, 3, 10), ("20,000", 2024, 3, 12)]
    说明表 = {
        "80,000": "银行转账,2024-03-08(证据二)",
        "10,000": "现金交付,借条出具当日2024-03-10(证据一、证据七;当事人陈述)",
        "20,000": "茶行账户转入被告指定公司账户,2024-03-12(证据五)",
    }
    for 金额串, y, m, d in 本金项:
        借款.append((Decimal(金额串.replace(",", "")), date(int(y), int(m), int(d)), 说明表.get(金额串, 金额串)))
    for (金额串, y, m, d), (e金额, ey, em, ed) in zip(本金项, 期望):
        ok = (金额串 == e金额 and (int(y), int(m), int(d)) == (ey, em, ed))
        输出(f"  {'✓' if ok else '✗'} 诉请二:以{金额串}元为基数自{y}年{m}月{d}日起"
             + ("" if ok else f"(与预期 {e金额}元/{ey}-{em}-{ed} 不符,请人工核对)"))
        if not ok:
            解析通过 = False

if len(月利率匹配) == 1 and 月利率匹配[0] == "1":
    月利率 = Decimal(月利率匹配[0]) / 100
    输出("  ✓ 利率:诉请二'均按月利率1%计算'→ 月利率 1%(该口径本身待定,见清单第4项)")
else:
    月利率 = None
    输出(f"  ✗ 月利率匹配异常:{月利率匹配}(应为['1']),中止计算。")
    解析通过 = False

if len(已还匹配) == 1 and 已还匹配[0] == ("2024", "9", "2"):
    已还额 = Decimal("5000")
    已还日 = date(2024, 9, 2)
    输出("  ✓ 已还款:诉请二'已付5,000元(2024年9月2日)依法先行抵充利息'→ 5,000元/2024-09-02"
         "(抵息系暂列口径,待定,见清单第3项)")
else:
    输出(f"  ✗ 已还款匹配异常:{已还匹配}(应为[('2024','9','2')]),中止计算。")
    解析通过 = False

待定标记 = ["抵充口径待定", "计息方案待定", "【起诉之日待定】", "款项性质待定"]
for 标记 in 待定标记:
    输出(f"  · {'检出' if 标记 in 诉紧 else '未检出'}待定标记:{标记}")
输出()

if not 解析通过:
    输出("【中止】参数解析未通过,本脚本不输出任何测算金额(避免以错参数算出看似可用的数)。")
    with open(os.path.join(BASE, 结果名), "w", encoding="utf-8") as f:
        f.write("\n".join(行) + "\n")
    sys.exit(1)

# ── 二、未决口径(不代选)──
截止 = 测算截止日 if 测算截止日 is not None else date.today()
输出("二、未决口径清单(本脚本不代选,仅分情景测算)")
输出("  1. 起诉日:起诉状为【起诉之日待定】,参数区'起诉日'未设定——本报告不含'诉请二"
     "应填金额'的选定值;如需暂计,请承办律师确定起诉日填入参数区重跑。")
输出(f"  2. 测算截止日:{中(截止)}"
     + ("(参数区显式指定)" if 测算截止日 is not None else "(未指定,取运行日)")
     + "。它仅为展示算式的截止点,不是选定的起诉日。")
输出("  3. 利率口径:月1%(诉请二暂列)与年10%(清单第4项待定)两情景均列出。")
输出("  4. 抵充口径:5,000元抵利息(诉请二暂列)与抵本金(清单第3项待定)两情景均列出。")
输出("  5. 3,000元(2024-12-31)性质:按诉状暂列口径不作抵减;若认定为还款需另行抵减重算,"
     "抵减口径未定,本脚本不代选(附参考数据)。")
输出(f"  6. LPR数值:情景D沿用原第5轮脚本的 {LPR备选_年利率:%}(待核,以起诉日适用报价为准)。")
输出()

# ── 三、情景测算 ──
输出(f"三、情景测算(展示截止:{中(截止)};天数口径:算头不算尾)")
输出()

输出("  情景A 月利率1% + 5,000元抵利息 + 3,000元按茶叶款不作抵减(诉请二暂列口径,"
     "该口径本身待定)")
毛利A = 打明细(截止, 月利率, "月")
截至抵充日 = 截至已生息(已还日, 月利率 / 30)
净利A = 毛利A - 已还额
输出(f"    利息合计(各笔四舍五入至分后加总):{毛利A:,.2f} 元")
输出(f"    减:{中(已还日)}已还 {已还额:,.0f} 元抵利息 → 净利息 {净利A:,.2f} 元")
输出(f"    (算术注:截至{中(已还日)}三笔已生息 {截至抵充日:,.2f} 元 ≥ 5,000 元,"
     "故抵充时点选择不影响本情景合计。)")
输出(f"    本金110,000元+净利息 = {Decimal('110000') + 净利A:,.2f} 元(抵充口径待定,见清单第3项)")
输出()

输出("  情景B 月利率1% + 5,000元抵本金(清单第3项待定口径)")
输出("    本金:105,000 元(5,000元自2024-09-02起从本金中扣减)")
输出("    【未决】5,000元从哪一笔本金中扣减、扣减后各笔如何计息,文书中未指明——"
     "本脚本不代选,不给出选定利息值。")
输出(f"    上限参考(即情景A抵充前利息 {毛利A:,.2f} 元;实际数应低于此值,"
     "待扣减口径确定后重算)。")
输出()

输出("  情景C '一分'按年利率10%理解 + 5,000元抵利息(清单第4项待定口径)")
毛利C = 打明细(截止, 年利率备用_一分按年10, "年")
净利C = 毛利C - 已还额
输出(f"    利息合计:{毛利C:,.2f} 元;抵利息后净利息:{净利C:,.2f} 元"
     "(若同时采抵本金口径,参照情景B处理,需重算)")
输出("    【口径注】本情景按年利率÷365折算天数;与情景A的月利率÷30口径不同——两套折算"
     "均为待定假设,全案统一口径待承办律师确定。")
输出()

输出("  情景D 不主张约定利息:自展期届满次日起按一年期LPR计逾期利息(起诉状备选方案,"
     "待承办律师确定)")
基数D = Decimal("105000")
LPR天数 = 天数(LPR备选_起算日, 截止)
LPR利息 = 舍(基数D * LPR备选_年利率 / 365 * LPR天数)
输出(f"    口径:以{基数D:,.0f}元为基数(该备选口径下2025-07-01前无约定利息,5,000元只能"
     "抵充本金)、自")
输出(f"    {中(LPR备选_起算日)} 起、按一年期LPR {LPR备选_年利率:%}"
     "【沿用原第5轮脚本数值,待核】计至")
输出(f"    {中(截止)},共 {LPR天数} 天")
输出(f"    算式:{基数D:,.0f} × {LPR备选_年利率:%} ÷ 365 × {LPR天数} = {LPR利息:,.2f} 元")
输出()

输出("  附注:3,000元(2024-12-31)若被认定为还款")
截至年末 = 截至已生息(date(2024, 12, 31), 月利率 / 30)
输出(f"    各情景均需另行抵减重算;抵利息还是抵本金、如何在三笔间分配,均未定,不代选。")
输出(f"    参考数据:截至{中(date(2024, 12, 31))}三笔已生息 {截至年末:,.2f} 元"
     "(月利率1%口径),供判断抵充次序时对照。")
输出()

# ── 四、与起诉日核对(如已设定)──
if 起诉日 is not None:
    输出(f"四、截至起诉日 {中(起诉日)} 的各情景金额(供诉请二填列选用,仍不代选口径)")
    gA = 打明细(起诉日, 月利率, "月")
    nA = gA - 已还额
    输出(f"    情景A净利息:{nA:,.2f} 元;本金+利息合计 {Decimal('110000') + nA:,.2f} 元")
    gC = 打明细(起诉日, 年利率备用_一分按年10, "年")
    输出(f"    情景C净利息(抵息):{gC - 已还额:,.2f} 元")
    dD = 天数(LPR备选_起算日, 起诉日)
    输出(f"    情景D:{基数D:,.0f} × {LPR备选_年利率:%} ÷ 365 × {dD} = "
         f"{舍(基数D * LPR备选_年利率 / 365 * dD):,.2f} 元")
    输出()
else:
    输出("四、起诉日核对:未执行(起诉状为【起诉之日待定】,参数区未设定,本脚本不代选)。")
    输出()

# ── 五、三份文书交叉一致性检查 ──
输出("五、与《证据目录-修正版.txt》《待核清单-修正版.txt》交叉核对")
核对项 = [
    ("6214****3317", 诉紧, "起诉状载8万元收款账号尾号"),
    ("6214****3317", 证紧, "证据目录证据二载同一账号尾号"),
    ("2024年9月2日", 诉紧, "起诉状载5,000元还款日"),
    ("2024年9月2日", 证紧, "证据目录证据四载同一还款日"),
    ("19时32分", 证紧, "证据目录载5,000元收款时刻19:32"),
    ("22时47分", 诉紧, "起诉状载3,000元发起时刻22:47"),
    ("22时47分", 证紧, "证据目录载同一发起时刻"),
    ("22时50分", 诉紧, "起诉状载3,000元收款时刻22:50"),
    ("22时50分", 证紧, "证据目录载同一收款时刻"),
    ("2024年3月12日", 诉紧, "起诉状载2万元交付日"),
    ("2024年3月12日9时47分", 证紧, "证据五载同一日及回单时刻09:47"),
    ("110,000", 清紧, "待核清单第3项本金口径110,000"),
    ("月利率1%", 清紧, "待核清单第4项月利率1%口径"),
    ("个体工商户", 清紧, "清单第1项已列茶行主体类型待核"),
    ("手指遮挡", 清紧, "清单第2项已按'手指遮挡'表述"),
    ("原告转述", 清紧, "清单第2项'曾住文三路'已标原告转述"),
    ("15.借条形式细节", 清紧, "清单新增第15项"),
    ("16.截图补齐与固定", 清紧, "清单新增第16项"),
    ("能否认定为自认", 清紧, "清单第12项已与起诉状、证据目录口径一致(不再写'自认已覆盖')"),
    ("的自认,待定", 诉紧, "起诉状对账段保留'能否认定为自认,待定'标注"),
    ("能否认定为自认", 证紧, "证据三第5项保留'待定'标注"),
]
全过 = True
for 片, 文, 说明 in 核对项:
    ok = 片 in 文
    全过 = 全过 and ok
    输出(f"  {'✓' if ok else '✗'} {说明}")
if not 全过:
    输出("  ✗ 存在未通过项:请先修正对应文书再重跑,本报告金额不受影响但口径一致性存疑。")
输出()

# ── 六、口径与假设 ──
输出("六、口径与假设(全部显式)")
输出("  1. 天数=两日期之差(算头不算尾:起息日计息、截止日不计息)。")
输出("  2. 情景A/B按'月利率÷30'折日利率;情景C/D按'年利率÷365'折算——两套折算并存,"
     "均为待定假设(清单未定统一口径)。")
输出("  3. 各笔利息先四舍五入至分再加总;金额均以起诉状诉请暂列口径为准。")
输出("  4. 本脚本只做算术与一致性核对,不构成对诉请可获支持程度的法律判断;"
     "所有【待定】事项以《待核与待定清单-修正版.txt》及承办律师决定为准。")
输出("  5. LPR数值沿用原第5轮脚本所列(待核),本脚本不联网、不更新任何外部数据。")
输出()
输出(f"—— 报告完(生成:{date.today().isoformat()};脚本:利息计算-修正版.py)——")

with open(os.path.join(BASE, 结果名), "w", encoding="utf-8") as f:
    f.write("\n".join(行) + "\n")
print(f"\n[已另存 {os.path.join(BASE, 结果名)}]")