Commit fce1db5e authored by wenwen.tang's avatar wenwen.tang 😕

feat(定时): 电费通知单改为 100% 手动发送,退出定时排期

口径(巴特菲 2026-10-13):电费通知单全部手动发送,不参与定时自动发送。

- PLANS 增加 `auto` 开关;elec 为 False、rent_parking 为 True
- 新增 `plans.AUTO_KEYS` / `plans.is_auto()`:定时器只跑 AUTO_KEYS
- `normalize()` 对 `auto=False` 的计划强制 `enabled=False`,历史配置写什么都保持关闭
- `plan_info()` 返回 state="manual",设置页给出「手动发送」说明与理由
- `status()` 增 `auto` / `manual_count`;`enabled` 对手动计划恒为 False
- `due_plans()` 只遍历 AUTO_KEYS;`fire()` 不带 plan_key 时同样只跑 AUTO_KEYS
  (设置页「手动触发」显式指定 plan_key 时才发该张)
parent f5014db6
......@@ -14,6 +14,11 @@
skip_rentfree 处于免租期的单元不通知
触发日 = 该账单的缴费期限(due_date) − advance_days,当日到达 send_time 后自动发送。
⚠️ 口径(2026-10-13 巴特菲):**电费通知单全部手动发送,不参与定时自动发送**。
计划里的 `auto=False` 表示「只保留免租期等设置,不排期、不自动发」——
定时器一律跳过这类计划,设置页也不给开启自动发送的入口。
房租车位费仍按「下一账单日 − 提前天数」自动出单并发送。
"""
import re
......@@ -28,6 +33,8 @@ PLANS = [
"desc": "按当期电费明细逐公司出单并发送",
"advance_days": 3,
"send_time": "09:00",
# 电费改为 100% 手动发送:不排期、不自动发(只保留「免租期内不通知」等设置)
"auto": False,
},
{
"key": "rent_parking",
......@@ -38,16 +45,24 @@ PLANS = [
"desc": "按台账逐单元出单并发送(账期起自「账单起始日」)",
"advance_days": 5,
"send_time": "09:00",
"auto": True,
},
]
BY_KEY = {p["key"]: p for p in PLANS}
KEYS = [p["key"] for p in PLANS]
# 参与定时自动发送的计划(`auto=False` 的只留设置,不排期)
AUTO_KEYS = [p["key"] for p in PLANS if p.get("auto", True)]
MAX_ADVANCE = 60 # 提前天数上限(含 0 = 缴费期限当天发)
_TIME_RE = re.compile(r"^([01]\d|2[0-3]):([0-5]\d)$")
def is_auto(key):
"""该账单是否参与定时自动发送。"""
return bool((BY_KEY.get(key) or {}).get("auto", True))
# ---------------------------------------------------------------- 默认值 / 归一化
def defaults():
"""每张账单的默认计划。"""
......@@ -103,6 +118,10 @@ def normalize(key, raw, fallback=None):
hh, mm = t.split(":")
if 0 <= int(hh) <= 23 and 0 <= int(mm) <= 59:
out["send_time"] = f"{int(hh):02d}:{int(mm):02d}"
# 不参与自动发送的账单(电费):无论历史配置写过什么,一律保持关闭
if not spec.get("auto", True):
out["enabled"] = False
return out
......
......@@ -4,8 +4,8 @@
============
后台线程每 30 秒巡检一次,按「两张账单」各自的计划逐张判断是否该发:
电费(elec) 触发日 = 该批次的缴费期限(due_date) − 提前天数;
到点把该批次里「待发送」的单据推出去。
电费(elec) **不自动发送**(2026-10-13 起全部手动)。
该计划 `auto=False`,定时器一律跳过;设置页也不提供开启入口。
房租车位费(rent_parking)
触发日 = 台账「下一账单日」(AJ) − 提前天数(**逐行判定**);
......@@ -162,6 +162,9 @@ def plan_info(key, pc, master, fired_date, now=None):
"""单张账单的计划运行状态(供设置页展示)。"""
now = now or dt.datetime.now()
spec = plans.meta(key) or {}
if not plans.is_auto(key):
return {"state": "manual", "next_at": "",
"note": "手动发送:电费通知单不参与定时自动发送,请在页面核对后手动发"}
if not master:
return {"state": "off", "next_at": "", "note": "定时发送总开关已关闭"}
if not pc.get("enabled"):
......@@ -222,11 +225,13 @@ def status():
for spec in plans.PLANS:
k = spec["key"]
pc = all_plans[k]
auto = plans.is_auto(k)
info = plan_info(k, pc, master, fired.get(k, ""), now)
items.append({
"key": k, "name": spec["name"], "short": spec["short"],
"column": spec["column"], "source": spec["source"],
"enabled": bool(pc.get("enabled")),
"auto": auto,
"enabled": bool(pc.get("enabled")) and auto,
"advance_days": pc.get("advance_days"),
"send_time": pc.get("send_time"),
"skip_rentfree": bool(pc.get("skip_rentfree")),
......@@ -237,6 +242,7 @@ def status():
"enabled": master,
"send_time": all_plans["elec"]["send_time"],
"enabled_count": sum(1 for i in items if i["enabled"]),
"manual_count": sum(1 for i in items if not i["auto"]),
"plans": items,
"fired": fired,
"last_fired_date": st.get("last_fired_date", ""),
......@@ -296,8 +302,12 @@ def _fire_one(key, reason):
def fire(reason="定时", plan_key=None):
"""执行一次发送。plan_key=None 表示逐张账单检查,各发各的待发单据。"""
keys = [plan_key] if plan_key else list(plans.KEYS)
"""执行一次发送。plan_key=None 表示逐张检查,各发各的待发单据。
不带 plan_key 时**只跑参与自动发送的账单**(电费已改为全手动,不在其中);
显式指定 plan_key(设置页「手动触发」)时才发该张。
"""
keys = [plan_key] if plan_key else list(plans.AUTO_KEYS)
agg = {"ok": 0, "fail": 0, "skipped": 0, "results": [], "plans": [],
"chat_name": "", "msg": ""}
......@@ -359,7 +369,7 @@ def due_plans(now=None):
fired = st.get("fired") or {}
today = now.strftime("%Y-%m-%d")
out = []
for k in plans.KEYS:
for k in plans.AUTO_KEYS:
pc = all_plans[k]
if not pc.get("enabled"):
continue
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment