跳转到内容

课时余额与预警

「课时余额与预警」是报读与课时台账点名与课消这两篇的收口:前两篇讲的是课时怎么进账、怎么被扣掉,这篇讲的是账面上现在还剩多少、什么时候该去催续费。产品里没有一个叫「余额」的字段能被直接改写——余额永远是从台账那本流水账里现算出来的,报表这一层只是把算好的结果、以及哪些学生该重点盯上,摆到一张名单上。

左侧图标栏 报表/reports),需要 report.view 权限,没有这个权限的账号打开会看到「你没有查看经营报表的权限,如需使用请联系管理员开通」。报表挂在「点名与课消」这块积木下,机构没开通这块积木,直达网址看到的是「此功能未开通」,不是 404。

报表页一共四个区块:教师课时、出勤率、课消与报读动态、余额与续费。本篇只讲最后这一块——页面顶部有一排「期间」「校区」筛选控件,对前三个区块都有效,但对「余额与续费」这一块,「期间」这个筛选形同虚设,具体原因见后面「预警名单」一节。

跟报读那篇讲的一样:hour_ledger 这张台账表本身没有余额列,每一行只记这一行自己的课时变动(delta),正数是充、负数是扣。报表要说清楚某个学生现在还剩多少课时,做法是把这个学生名下的全部台账行按「学生 × 学科」分组,把每一组里的 delta 加起来——报读、期初、调整(人手写的三种入账)和课消(点名产生的扣减)全部算在内,不区分是哪一种类型。这跟点名工作台旁边、学生详情页台账卡片上看到的余额是同一套算法、同一份数据,报表不会另起一份自己的数字,也不会缓存一份滞后的结果。

分桶是什么意思

章节:分桶是什么意思

分桶的规则跟台账那篇完全一致:每一行台账都挂着一个学科,算余额时按「这个学生 + 这个学科」分组求和,数学桶和英语桶互不冲抵。学科字段留空的那一桶,报表上显示为「通用课时」——源码里的判定就是「subjectIdnull 就归进这一桶」,不是某一门叫「通用」的学科。「余额与续费」这张表的「分桶余额」这一列,就是把这个学生名下所有非空的桶和这个通用课时桶,逐个列出来,中间用 / 隔开。

「余额与续费」这个区块标题下面(表格上面)会先打一行提示,字面是「预警阈值:当前余额 ≤ 5 课时」——5 是默认值,来自机构级的考勤设置(attendance_settings 表的 balance_alert_threshold 列,domains/attendance/src/settings.ts 里连同缺勤/请假扣不扣课时一起维护,无行时按默认值 5 走,这个阈值可以由具备设置权限的账号改,改了之后新打开的报表立刻按新值判定,不影响任何历史台账行)。

进不进这张名单,判的不是「合计余额 ≤ 阈值」这一条,而是任一分桶或合计,两条只要占一条就算:哪怕一个学生数学桶还有一大把结余,只要他的英语桶单独低到阈值以下,他照样会出现在这张名单里——报表的意图是「催续费」,一个学科快没课时了就该提醒,不能因为另一个学科还有富余就把这条提醒盖掉。名单只收「有过至少一行台账记录」的在读学生:一个从没报过读、一行台账都没有的学生不会被当成「余额是 0 所以要预警」塞进来,这样才不会把一份行动清单变成半个学生通讯录。

名单按合计余额从低到高排(欠得最多的排最前),列出学生、合计余额、分桶余额、最近报读日期、最近出勤日期这五列;日期查不到时显示「—」。名单里的学生名是一个链接,点进去就是这个学生的详情页——报读入口(「报读 / 课时台账」那张卡片)就在那一页上,报表本身是只读页,点进去走的是同一个标签页,不会另开新窗口。名单空的时候显示「当前没有余额触及阈值的在读学生」,不是加载失败。

经营报表「余额与续费」区块截图,白卡片浮在留白背景上:预警阈值提示当前余额 ≤5 课时,李梓涵一行合计余额与通用课时分桶均为 -3.00 并标红

上面这张图里,李梓涵合计余额是 -3.00,分桶余额那一列的「通用课时」同样是 -3.00——这名学生名下只有通用课时这一个桶,所以合计和这一个桶的数字对得上,两处都标了红。

为什么合计是正的也会标红

章节:为什么合计是正的也会标红

这是最容易被想岔的一条:进不进预警名单,用的是上面那条「任一分桶或合计 ≤ 阈值」,阈值默认 5、可以改;但表格里哪一格显示成红字,用的是另一条完全独立、不能配置的规则——「当前余额 ≤ 0」,不是「负数才标红」,恰好归零的桶或合计同样标红。而且这条判断是逐格算的:合计那一格红不红,只看合计这个数字本身够不够低;某个分桶那一格红不红,只看那一个桶自己的数字,跟合计是多少没有关系。

这就带出一种反直觉但完全合理的形态:一个学生的合计余额是正的(比如某个学科的桶里还攒着一大笔课时),可另外一个学科的桶已经因为一笔调整或者用超了变成了负数或零——这种情况下,合计那一格因为本身是正数、不会标红,但那个已经欠费的分桶会单独标红,两个判断互不影响。这样设计不是疏忽:报表按学科分桶的意义就是让教务一眼看出「这个学生具体是哪门课快用完了」,如果只看合计,一门课已经在倒扣、被另一门课的富余抵掉,账面上风平浪静,实际上这门课的下一节该学生一旦点名出勤就会继续往负里扣,越欠越多却始终不会被合计这个数字暴露出来。分桶红标就是补上这个盲区的——它不管全局账面好不好看,只管这一个桶自己有没有欠费。

举例(这里是纯数字演算,不是某个真实学生的台账):某学生数学桶余额 +15.00、英语桶余额 −2.00,合计 +13.00。合计这一格因为 13.00 > 0 不标红;英语这一格因为 −2.00 ≤ 0 单独标红;同时因为英语桶已经 ≤ 5(预警阈值),这个学生仍然会出现在预警名单里——哪怕合计看起来很健康,报表照样把他放进催费名单,也照样把出问题的那个桶标红给你看。

为什么我把报表页顶部的期间筛选从「本月」换成「上月」,这份名单一点没变?「余额与续费」这一块本来就不看期间——这个区块标题下面那句提示写得很直白:「本区块是当前余额快照,不随期间筛选变化(期间只用于导出文件名)」。名单反映的是打开报表那一刻账上的余额,不是某一段时间里发生了什么;期间筛选只会影响导出的 CSV 文件名里带的日期区间,不会让名单本身多一行或者少一行。

预警阈值能不能改,改了会不会影响以前的记录? 能改,阈值是机构级的设置项,默认 5 课时;改了之后只影响下一次打开报表时的判定标准,不会重算或者标记任何历史台账行——台账本身从不因为阈值变化而改变。

通用课时桶会不会单独触发预警? 会。通用课时只是「学科字段留空」的那一类台账行汇总出来的一个桶,判定规则跟任何一个具体学科的桶完全一样——它自己的余额低到阈值以下,同样会让这个学生进名单;它自己的余额 ≤ 0,同样会单独标红。

一个从来没报过读的新学生,会不会因为余额是 0 被拉进预警名单? 不会。名单只收「名下至少有一行台账记录」的学生——没报过读、没有期初导入、也没被点过课消的学生,根本不在报表统计的候选范围里,不会被当成「余额 0,该催费」处理。

名单里点学生名字会跳到哪? 跳到这个学生的详情页,「报读 / 课时台账」卡片就在那一页上,续费、报读、课时调整都从那里操作——报表页本身不提供任何写入入口。