"""HR 假期域通知模板 —— 年假到期(清零)提醒。 一个 key:``hr_leave.expiry_reminder``。HR 在「年假到期提醒」页面圈人后 群发,收件人是员工本人。文案是 HR 的编辑对象:这里的三语内容只是**出厂 默认值**,HR 通过 ``PUT /hr/leave/expiry-reminder/config`` 写 ``platform_settings['email_template.hr_leave.expiry_reminder']`` 覆盖, 覆盖是字段级合并(``_load_template``),所以 HR 只改中文不会把英日文案清空。 **这一版正文是 HR 逐字给的**(2026-08-18),因此周期是写死的:「2025 年 4 月 1 日发放的 FY26 额度、2026 年 9 月 30 日到期、10 月 1 日清零」。它描述的是一个 具体周期,不是一句可复用的通用话术 —— 下一个周期 HR 要在页面上改这段(标题 里的日期走 ``{deadline_date}``,会自己跟着名单走,正文的中文日期不会)。 Placeholder 契约(``leave_reminder_service._build_context`` 逐人填充, ``leave_reminder_service.ALLOWED_PLACEHOLDERS`` 是 HR 编辑时的白名单): * ``person_name`` —— 收件人显示名 * ``account_lines`` —— 逐个假期账户的多行明细,**按收件人语言渲染** (一个 envelope 只有一个收件人,所以 context 可以是本地化后的成品) * ``total_balance_days`` —— 命中账户的剩余合计 * ``expiring_balance_days`` —— **最早那个清零日**会带走多少天(与上面一条区分: 两个数并列而不加限定语时,员工会读成「合计 13 天全部在 9/30 清零」) * ``as_of_date`` —— 余额的口径日期(发送时的快照;不写出来员工就会拿它和实时 卡片的差额来问 HR) * ``deadline_date`` —— 最早清零日(ISO,如 ``2026-09-30``) * ``deadline_month`` —— 最早清零日所在月份,按语言渲染(``9 月`` / ``September`` / ``9月``) * ``expires_in_days`` —— 距最早清零日的天数 * ``hris_url`` —— HR 自己配的 HRIS 请假入口(未配置时是空串,所以默认 文案不把它写进正文,而是走卡片按钮) * ``cta_line`` / ``portal_url`` —— 发送路径自动注入 **默认正文刻意不写任何余额数字**(HR 决定,2026-08-18):数字只出现在飞书卡片 的额度格子里,邮件正文是纯说明。``{account_lines}`` 等占位符仍然可用,HR 想把 明细写回正文随时可以。 **默认正文也不带 ``{cta_line}``**:卡片有按钮,而邮件里那行 Helix 链接 HR 明确 要求去掉(「界面会整洁一些」)。 余额数字取「发送那一刻」的快照(见 ``leave_reminder_service.resolve_cohort``),之后员工休了假卡片里的数字 不会变——这是刻意的:通知是当时的告知记录,不是实时看板。 **没有 ``card_subtitle``**:那行「余额来自 HRIS(SuccessFactors)同步」的灰字 HR 要求关掉(2026-08-18)。模板里没有这个 key ⇒ ``_resolve_card_chrome`` 拿不到 覆盖值 ⇒ 卡片不渲染副标题那一行。HR 之后想要一句副标题,在文案编辑器里填上 ``card_subtitle`` 即可(字段仍然是可编辑的,只是出厂为空)。 """ from __future__ import annotations TEMPLATES: dict[str, dict[str, str]] = { "hr_leave.expiry_reminder": { # 标题里的日期走占位符:HR 给的原文是「你的 FY26 年假将于 2026-09-30 # 到期」,而 ``{deadline_date}`` 渲染出来一模一样,却会跟着这一批名单 # 的真实清零日走(HR 换个财年/窗口再发时不至于写着去年的日期)。 "subject": "你的 FY26 年假将于 {deadline_date} 到期", "subject_en": "Your FY26 annual leave expires on {deadline_date}", "subject_ja": "FY26 の年次有給休暇は {deadline_date} に失効します", "body_text": ( "Hi {person_name},你的年假即将到期啦!\n\n" "2025 年 4 月 1 日发放的 FY26 法定年假 / 福利年假,有效期至 2026 年 " "9 月 30 日;未休完的额度将于 2026 年 10 月 1 日起清零作废。" "为方便提前规划,现就相关安排提醒如下:\n\n" "✅ FY26 周期年假额度最晚只能用于 9 月 30 日(含当日)的休假;\n" "✅ 9 月申请 10 月及以后的休假,将扣减 FY27 周期(2026 年 4 月 1 日" "发放)额度,FY26 周期剩余额度仍会到期清零;\n" "✅ 请于 9 月 30 日下班前完成申请提交与审批,休假前请做好工作交接," "确保团队业务正常运转。\n\n" "以上余额截至 {as_of_date}。Helix 中的数据仅作参考,如有出入," "请以 HRIS 系统实时记录为准。如有相关问题,请咨询 HR Helpdesk。" ), "body_text_en": ( "Hi {person_name}, your annual leave is about to expire.\n\n" "Your FY26 statutory and welfare annual leave, granted on " "1 April 2025, is valid through 30 September 2026; any balance you " "have not used is forfeited from 1 October 2026. To help you plan " "ahead:\n\n" "✅ FY26 leave can only cover time off taken on or before " "30 September (inclusive);\n" "✅ A request filed in September for dates in October or later " "draws on your FY27 balance (granted 1 April 2026) instead — your " "remaining FY26 balance is still forfeited;\n" "✅ Please submit AND get your request approved before end of " "business on 30 September, and hand your work over before you go so " "your team keeps running.\n\n" "Balances are as of {as_of_date}. Figures in Helix are for " "reference only; where they differ, the live record in HRIS " "prevails. For related questions, please ask the HR Helpdesk." ), "body_text_ja": ( "{person_name} さん、年次有給休暇の有効期限が近づいています。\n\n" "2025 年 4 月 1 日に付与された FY26 の法定年休・福利年休は 2026 年 " "9 月 30 日まで有効です。取得しなかった残日数は 2026 年 10 月 1 日" "をもって失効します。計画的にご取得いただくため、以下をご確認" "ください。\n\n" "✅ FY26 の残日数を充当できるのは 9 月 30 日(当日を含む)" "までの休暇です;\n" "✅ 9 月に 10 月以降の日程を申請すると FY27(2026 年 4 月 1 日" "付与)の残日数から消費され、FY26 の残日数は期限どおり失効します;\n" "✅ 9 月 30 日の終業までに申請と承認を完了し、休暇前には業務の" "引き継ぎを済ませ、チームの業務に支障が出ないようご配慮ください。\n\n" "残日数は {as_of_date} 時点です。Helix の数値は参考値であり、" "相違がある場合は HRIS の最新記録が優先されます。" "ご不明な点は HR Helpdesk までお問い合わせください。" ), # 卡片上唯一的按钮 —— 指向 HR 配置的 SF 请假入口(见 # ``leave_reminder_service.build_card_actions``)。 "card_button_label": "查看我的假期", "card_button_label_en": "View my leave balance", "card_button_label_ja": "休暇残高を見る", }, } __all__ = ["TEMPLATES"]