"""入离职 / 资产流转域通知模板。 覆盖的流程: * **借用与资产流转** —— 借用超期催还、离职持有周报、离职当日的自动转让 / 回收至办公室 / 借出资产自动归还、T+0 落入「离职持有」的 IT 兜底提醒。 * **离职收尾** —— 未接入自动化的软件由负责人手动关号、Leaver Blocked 双签 override 的三个节点(副签请求 / 副签通过 / 副签驳回)。 * **入职** —— Day-90 入职体验问卷。 * **平台日常** —— IT 生命周期日报、HRIS 小增量自动确认、T+1 合规残留告警、 转岗后云授权 pending_review 超期。 每个 ``{placeholder}`` 都必须由调用点的 ``NotificationEnvelope.context`` 提供:铃铛会用 context 重新渲染标题和正文,context 缺 key 时用户会看到字面量 ``{foo}``。本次补模板时相应补齐了调用点的 context(见各 key 上方注释)。 ------------------------------------------------------------------------ 故意 **不** 注册的 key(不是遗漏) ------------------------------------------------------------------------ ``hire_day_it_info.feishu_card`` / ``hire_day_it_info.hr_cc``。 入职信主体(发给新人本人的 Day-1 邮件)用的是 ``hire_day_it_info``,它早已 在 ``service.py`` 的 ``_DEFAULT_TEMPLATES`` 里注册且三语齐全 —— 不在本次范围。 这里说的是它的另外两个投递副本: * ``.feishu_card`` —— Day-1 飞书欢迎卡片。正文与卡片模块由 ``_build_welcome_card_fields`` 按 ``is_en = not person.is_china_employee`` 组装(HR Ops 2026-07-15 决策:入职信语言看劳动合同主体)。模板渲染却是按 ``app.core.locale.resolve_lang(recipient)`` 选语言(优先 preferred_language) —— 注册模板会让 subject/body_text 走一套语言口径、而同一张卡的 stat / section 走另一套,并把卡片正文整段顶掉(``_render_for_recipient`` 里模板 优先)。 * ``.hr_cc`` —— 抄送 HRBP / HR Ops 的同一封入职信副本。它**故意**用新人的 语言渲染("byte-identical to what the hire received"),标题还带 「[新人入职] {姓名} · 」前缀。注册模板会按 **HR 收件人**的语言重渲染并丢掉 前缀,正好破坏这条链路的语义。 两条都是 ``hidden_from_inbox=True``,不进铃铛,所以连「铃铛按收件人语言重 渲染」这点收益也拿不到。要真正把这两个副本做成三语,应当在 ``_build_welcome_card_fields`` 里按 ``resolve_lang`` 增加 ja 分支,并同步确认 DECISIONS.md 里「语言看合同主体」的口径 —— 那是产品决策变更,不属于本次 「模板化 + 翻译」的范围。 """ from __future__ import annotations TEMPLATES: dict[str, dict[str, str]] = { # ── 借用 / 资产流转 ────────────────────────────────────────── # asset_inventory_handlers.py::borrow_overdue_reminder —— 调用点原本没有 # context(标题正文全部 f-string 硬编码中文),本次补上 # asset_label / asset_name / expected_return_date / days_overdue。 # ── 借用延期 ──────────────────────────────────────────────── # asset/service.py 用多行条件表达式拼 key(``template_key=`` 与字面量不在 # 同一行),两个 key 一直没被闸门看见 ⇒ 借用人(员工本人!)收到的一直是中文。 # context: asset_tag, asset_label, borrower_name, expected_return_date, # previous_expected_return_date, reason, recipient_role "asset.borrow_extension.borrower": { "subject": "[借用延期] 预计归还时间已调整为 {expected_return_date}", "subject_en": "[Loan extended] New expected return date: {expected_return_date}", "subject_ja": "[貸出延長] 返却予定日が {expected_return_date} に変更されました", "body_text": ( "你借用的『{asset_label}』({asset_tag})预计归还时间已调整为 " "[b]{expected_return_date}[/b](原 {previous_expected_return_date})。\n" "说明:{reason}\n\n" "{cta_line}\n\n" "— Helix IT 平台" ), "body_text_en": ( "The expected return date for \"{asset_label}\" ({asset_tag}), " "which you have on loan, is now [b]{expected_return_date}[/b] " "(was {previous_expected_return_date}).\n" "Note: {reason}\n\n" "{cta_line}\n\n" "— Helix IT Platform" ), "body_text_ja": ( "お借りいただいている「{asset_label}」({asset_tag})の返却予定日が " "[b]{expected_return_date}[/b] に変更されました" "(変更前 {previous_expected_return_date})。\n" "備考:{reason}\n\n" "{cta_line}\n\n" "— Helix IT プラットフォーム" ), "card_button_label": "查看我的资产", "card_button_label_en": "View my assets", "card_button_label_ja": "自分の資産を見る", }, "asset.borrow_extension.stakeholder": { "subject": "[借用延期] {borrower_name} 延长至 {expected_return_date}", "subject_en": ( "[Loan extended] {borrower_name} extended to {expected_return_date}" ), "subject_ja": ( "[貸出延長] {borrower_name} さんが {expected_return_date} まで延長" ), "body_text": ( "{borrower_name} 将借用的『{asset_label}』({asset_tag})预计归还时间" "延长至 [b]{expected_return_date}[/b]" "(原 {previous_expected_return_date})。\n" "原因:{reason}\n\n" "{cta_line}\n\n" "— Helix IT 平台" ), "body_text_en": ( "{borrower_name} extended the expected return date of " "\"{asset_label}\" ({asset_tag}) to [b]{expected_return_date}[/b] " "(was {previous_expected_return_date}).\n" "Reason: {reason}\n\n" "{cta_line}\n\n" "— Helix IT Platform" ), "body_text_ja": ( "{borrower_name} さんが「{asset_label}」({asset_tag})の返却予定日を " "[b]{expected_return_date}[/b] まで延長しました" "(変更前 {previous_expected_return_date})。\n" "理由:{reason}\n\n" "{cta_line}\n\n" "— Helix IT プラットフォーム" ), "card_button_label": "查看资产", "card_button_label_en": "View the asset", "card_button_label_ja": "資産を見る", }, "asset.borrow_overdue": { "subject": "⏰ 借用资产已超期 —— {asset_label}", "subject_en": "⏰ Borrowed asset overdue — {asset_label}", "subject_ja": "⏰ 貸出資産の返却期限超過 —— {asset_label}", "body_text": ( "你借用的资产 [b]{asset_label}[/b]({asset_name})已超过预定归还日期 " "{expected_return_date},目前已超期 [r]{days_overdue}[/r] 天。\n\n" "请尽快完成归还;还需继续使用请联系 IT 申请顺延;资产若已丢失," "请联系 IT 走丢失流程。\n\n" "{cta_line}\n\n— Helix IT 平台" ), "body_text_en": ( "The asset [b]{asset_label}[/b] ({asset_name}) you borrowed is past " "its expected return date {expected_return_date} — days overdue: " "[r]{days_overdue}[/r].\n\n" "Please return it as soon as you can. If you still need it, contact " "IT to extend the loan; if it is lost, contact IT to start the loss " "process.\n\n" "{cta_line}\n\n— Helix IT Platform" ), "body_text_ja": ( "お借りいただいている資産 [b]{asset_label}[/b]({asset_name})が" "返却予定日 {expected_return_date} を [r]{days_overdue}[/r] 日超過" "しています。\n\n" "お早めにご返却ください。引き続きご利用が必要な場合は IT まで延長を" "ご依頼ください。紛失された場合も IT までご連絡をお願いします。\n\n" "{cta_line}\n\n— Helix IT プラットフォーム" ), "card_button_label": "去归还", "card_button_label_en": "Return the asset", "card_button_label_ja": "返却手続きへ", }, # asset_inventory_handlers.py::held_by_leaver_weekly_reminder —— context # 原本只有 held_count + samples(list);本次补 sample_block(渲染好的 # 多行文本),list 无法直接插值成人类可读的清单。 "asset.held_by_leaver_reminder": { "subject": "[资产] {held_count} 件离职员工持有的资产待追回", "subject_en": "[Assets] Still held by leavers: {held_count}", "subject_ja": "[資産] 退職者が保持したままの資産 {held_count} 件", "body_text": ( "当前有 [b]{held_count}[/b] 件资产标记为「离职持有」:仍归公司所有," "但物理上由已离职员工保管。\n\n" "{sample_block}\n\n" "处理建议:确认归还后,在资产详情页点「追回」即可把状态翻回闲置。\n\n" "{cta_line}\n\n— Helix IT 平台" ), "body_text_en": ( "Assets marked \"held by leaver\": [b]{held_count}[/b]. They are " "still company property, but physically with employees who have " "already left.\n\n" "{sample_block}\n\n" "Next step: once an asset is handed back, open its detail page and " "click \"Recover\" to flip it back to in-stock.\n\n" "{cta_line}\n\n— Helix IT Platform" ), "body_text_ja": ( "現在 [b]{held_count}[/b] 件の資産が「退職者保持」の状態です。会社の" "資産のまま、退職済みの従業員の手元に残っています。\n\n" "{sample_block}\n\n" "対応方法:返却を確認できた資産は、資産詳細ページの「回収」から" "在庫状態に戻してください。\n\n" "{cta_line}\n\n— Helix IT プラットフォーム" ), "card_button_label": "查看资产", "card_button_label_en": "Open asset list", "card_button_label_ja": "資産一覧を開く", }, # advance_service.py::_notify_transfers —— 出借人侧:借用人离职,资产自动 # 归还回出借人名下。context 已有 recipient_name / leaver_name / count / # asset_list。 "borrow_auto_returned": { "subject": "[借用归还] {leaver_name} 离职,你出借的 {count} 项资产已自动归还", "subject_en": ( "[Loan returned] {leaver_name} has left — assets returned to you: " "{count}" ), "subject_ja": ( "[貸出返却] {leaver_name} さんの退職に伴い、貸し出していた資産 " "{count} 件が自動返却されました" ), "body_text": ( "{recipient_name},员工 [b]{leaver_name}[/b] 今日离职,以下向你借用的" "资产已由系统自动归还到你名下:\n\n" "{asset_list}\n\n" "你可以在 Helix 的个人页面看到这些资产。\n\n" "{cta_line}\n\n— Helix IT 平台" ), "body_text_en": ( "{recipient_name}, [b]{leaver_name}[/b] leaves today. Everything " "they had borrowed from you was returned to your name " "automatically:\n\n" "{asset_list}\n\n" "You can find them on your personal page in Helix.\n\n" "{cta_line}\n\n— Helix IT Platform" ), "body_text_ja": ( "{recipient_name} さん、[b]{leaver_name}[/b] さんが本日退職されるため、" "貸し出されていた以下の資産をシステムが自動的にお戻ししました:\n\n" "{asset_list}\n\n" "Helix の個人ページからご確認いただけます。\n\n" "{cta_line}\n\n— Helix IT プラットフォーム" ), }, # advance_service.py::_notify_transfers —— 接收人侧(经理兜底 / 默认 IT # 联系人等)。调用点原本把相对路径写进 context["portal_url"],会盖掉渲染器 # 注入的绝对地址、让邮件 CTA 变成不可点的相对路径,本次移除。 "force_transfer_received": { "subject": "[自动转让] {leaver_name} 的 {count} 项资产已转给你", "subject_en": ( "[Auto transfer] Assets from {leaver_name} now assigned to you: " "{count}" ), "subject_ja": "[自動移管] {leaver_name} さんの資産 {count} 件が移管されました", "body_text": ( "{recipient_name},员工 [b]{leaver_name}[/b] 今日离职,以下资源已由" "系统自动转到你名下(无需你确认接受):\n\n" "{asset_list}\n\n" "请到 Helix 确认,或再次转让给实际使用人。\n\n" "{cta_line}\n\n— Helix IT 平台" ), "body_text_en": ( "{recipient_name}, [b]{leaver_name}[/b] leaves today. The following " "resources were transferred to you automatically (no acceptance " "needed):\n\n" "{asset_list}\n\n" "Please confirm them in Helix, or pass them on to whoever actually " "uses them.\n\n" "{cta_line}\n\n— Helix IT Platform" ), "body_text_ja": ( "{recipient_name} さん、[b]{leaver_name}[/b] さんが本日退職されるため、" "以下のリソースがシステムにより自動的にあなたへ移管されました" "(受領操作は不要です):\n\n" "{asset_list}\n\n" "Helix でご確認いただくか、実際の利用者へ再移管してください。\n\n" "{cta_line}\n\n— Helix IT プラットフォーム" ), "card_button_label": "查看我的待办", "card_button_label_en": "Open my to-dos", "card_button_label_ja": "自分のタスクを開く", }, # advance_service.py::_notify_transfers —— 办公室管理员侧:资产回收进办公室 # 库存,不是转到个人名下,所以文案是「入库 / 盘点」而非「已转给你」。 "offboarding.recycled_to_office": { "subject": "[资产回收] {leaver_name} 的 {count} 项资产已回收至办公室", "subject_en": ( "[Asset recycled] Assets from {leaver_name} returned to the " "office: {count}" ), "subject_ja": ( "[資産回収] {leaver_name} さんの資産 {count} 件をオフィスに回収しました" ), "body_text": ( "{recipient_name},员工 [b]{leaver_name}[/b] 今日离职,以下资产已由" "系统回收至办公室,请安排入库 / 盘点:\n\n" "{asset_list}\n\n" "{cta_line}\n\n— Helix IT 平台" ), "body_text_en": ( "{recipient_name}, [b]{leaver_name}[/b] leaves today. The following " "assets were recycled into office inventory — please put them into " "stock and check them in:\n\n" "{asset_list}\n\n" "{cta_line}\n\n— Helix IT Platform" ), "body_text_ja": ( "{recipient_name} さん、[b]{leaver_name}[/b] さんが本日退職されるため、" "以下の資産をオフィス在庫として回収しました。入庫・棚卸をお願い" "します:\n\n" "{asset_list}\n\n" "{cta_line}\n\n— Helix IT プラットフォーム" ), "card_button_label": "去资产列表", "card_button_label_en": "Open asset list", "card_button_label_ja": "資産一覧を開く", }, # advance_service.py —— T+0 落入「离职持有」的当日 IT 提醒(与每周的 # asset.held_by_leaver_reminder 汇总不同,这条是即时 FYI)。 "offboarding.t0_default_held_by_leaver": { "subject": "[T+0 离职持有] {leaver_name} 的 {count} 项资产标记为离职持有待追回", "subject_en": ( "[T+0 held by leaver] Assets of {leaver_name} awaiting IT " "recovery: {count}" ), "subject_ja": ( "[T+0 退職者保持] {leaver_name} さんの資産 {count} 件が回収待ちです" ), "body_text": ( "员工 [b]{leaver_name}[/b]({leaver_email})今日 T+0,以下 {count} 项" "资产已落入「离职持有」状态(离职前 IT 与员工均未处置、走了默认兜底," "或回收办公室失败降级),仍留在该员工手上,不阻塞离职流程:\n\n" "{asset_list}\n\n" "处理建议:确认归还 / 回购后,在资产详情页点「追回」把状态翻回闲置;" "也可以在该离职流程详情页补一个 disposition。\n\n" "{cta_line}\n\n— Helix IT 平台" ), "body_text_en": ( "[b]{leaver_name}[/b] ({leaver_email}) reaches T+0 today. Assets " "that ended up \"held by leaver\": {count}. Either nobody chose a " "disposition before the leaver date so the default applied, or the " "office recycle could not resolve an office and degraded. They " "stay with the leaver and do not block the offboarding:\n\n" "{asset_list}\n\n" "Next step: once an asset is handed back or bought back, click " "\"Recover\" on its detail page to flip it to in-stock; you can also " "add a disposition on the offboarding process.\n\n" "{cta_line}\n\n— Helix IT Platform" ), "body_text_ja": ( "[b]{leaver_name}[/b]({leaver_email})さんが本日 T+0 を迎えました。" "以下の {count} 件の資産が「退職者保持」となっています(退職日までに" "処置が選ばれずデフォルトが適用された、またはオフィス回収先を特定" "できず降格したケースです)。資産は本人の手元に残りますが、退職" "フローは止まりません:\n\n" "{asset_list}\n\n" "対応方法:返却または買い取りを確認できたら、資産詳細ページの" "「回収」で在庫状態に戻してください。退職フロー側で処置を追加する" "こともできます。\n\n" "{cta_line}\n\n— Helix IT プラットフォーム" ), }, # offboarding/service.py::_notify_owner_manual_close —— 未接入 Helix 自动化 # 的软件,由负责人手动关号。调用点原本在 context 里塞了一句硬编码中文的 # cta_line,注册模板后由模板自己承担这句话,故移除。此处刻意不用 # {cta_line}/{portal_url}:envelope 的 url 是厂商登录页(可能为 None), # 「请到 Helix 查看」的通用 CTA 会指错地方。 "offboarding.owner_manual_close": { "subject": "⚠️ 有员工离职 —— 请手动关闭「{software_name}」账号", "subject_en": ( "⚠️ Employee left — please close their {software_name} account " "manually" ), "subject_ja": "⚠️ 退職者発生 —— {software_name} のアカウント手動停止のお願い", "body_text": ( "{owner_name},[b]{leaver_name}[/b]({leaver_email})已于 " "[r]{leaver_date}[/r] 离职,其在「{software_name}」的账号需要你作为" "负责人[b]手动关闭[/b] —— 该系统未接入 Helix 自动化,平台无法代为" "回收。\n\n" "请尽快登录该系统停用 / 删除对应账号。\n\n— Helix IT 平台" ), "body_text_en": ( "{owner_name}, [b]{leaver_name}[/b] ({leaver_email}) left on " "[r]{leaver_date}[/r]. As the registered owner of " "\"{software_name}\", you need to [b]close their account " "manually[/b] — that system is not wired into Helix automation, so " "the platform cannot reclaim it for you.\n\n" "Please sign in to that system and disable or delete the account.\n\n" "— Helix IT Platform" ), "body_text_ja": ( "{owner_name} さん、[b]{leaver_name}[/b]({leaver_email})さんが " "[r]{leaver_date}[/r] に退職されました。「{software_name}」の担当者" "として、アカウントの[b]手動停止[/b]をお願いします。当該システムは " "Helix の自動化に接続されていないため、プラットフォーム側では回収" "できません。\n\n" "お手数ですが、当該システムにログインのうえ、対象アカウントの無効化" "または削除をお願いします。\n\n— Helix IT プラットフォーム" ), }, # ── Leaver Blocked 双签 override ───────────────────────────── # override_service.py::_notify_secondary —— context 本次补 blocker_list # (渲染好的 blocker 清单)与 reason(主签理由节选),并把 expires_at 的 # None 收敛成 "N/A" 字面量,避免正文出现 Python 的 None。 "leaver_blocked.dual_sign.secondary_request": { "subject": ( "[副签请求] {primary_name} 请你共同签名跳过 {leaver_name} 的 " "{blocker_count} 项离职 blocker" ), "subject_en": ( "[Dual-sign request] {primary_name} asks you to co-sign an " "offboarding-blocker skip for {leaver_name} — blockers: " "{blocker_count}" ), "subject_ja": ( "[副署依頼] {primary_name} さんが {leaver_name} さんの退職ブロッカー " "{blocker_count} 件のスキップに副署を求めています" ), "body_text": ( "IT 同事 [b]{primary_name}[/b] 申请绕开下列 blocker,让 " "[b]{leaver_name}[/b] 完成离职:\n\n" "{blocker_list}\n\n" "IT 给出的理由(节选):{reason}\n\n" "过期时间:[r]{expires_at}[/r]\n\n" "副签前请确认:业务上确实可以放行,且你愿意为此承担合规责任。\n\n" "{cta_line}\n\n— Helix IT 平台" ), "body_text_en": ( "[b]{primary_name}[/b] from IT requests an override of the " "following blockers so that [b]{leaver_name}[/b] can complete " "offboarding:\n\n" "{blocker_list}\n\n" "Stated reason (excerpt): {reason}\n\n" "Expires: [r]{expires_at}[/r]\n\n" "Before you co-sign, confirm that the release is acceptable on the " "business side and that you accept the compliance responsibility " "for it.\n\n" "{cta_line}\n\n— Helix IT Platform" ), "body_text_ja": ( "IT の [b]{primary_name}[/b] さんが、[b]{leaver_name}[/b] さんの退職" "手続きを完了させるため、以下のブロッカーの回避を申請しています:\n\n" "{blocker_list}\n\n" "申請理由(抜粋):{reason}\n\n" "有効期限:[r]{expires_at}[/r]\n\n" "副署の前に、業務上リリースして問題がないこと、そのコンプライアンス" "責任を負えることをご確認ください。\n\n" "{cta_line}\n\n— Helix IT プラットフォーム" ), "card_button_label": "去审阅", "card_button_label_en": "Review the request", "card_button_label_ja": "内容を確認する", }, # override_service.py::_notify_primary_can_execute "leaver_blocked.dual_sign.execute_ready": { "subject": "[副签已通过] 你可以执行 {leaver_name} 的离职 override", "subject_en": ( "[Co-signed] You can now execute the offboarding override for " "{leaver_name}" ), "subject_ja": ( "[副署完了] {leaver_name} さんの退職オーバーライドを実行できます" ), "body_text": ( "副签人已批准。你现在可以到 Helix 执行该 override —— 执行后系统会" "忽略指定的 blocker,并把 [b]{leaver_name}[/b] 置为已离职。\n\n" "{cta_line}\n\n— Helix IT 平台" ), "body_text_en": ( "The co-signer approved. You can now execute the override in " "Helix — it will ignore the listed blockers and set " "[b]{leaver_name}[/b] to left.\n\n" "{cta_line}\n\n— Helix IT Platform" ), "body_text_ja": ( "副署者が承認しました。Helix でオーバーライドを実行できます。実行" "すると指定のブロッカーは無視され、[b]{leaver_name}[/b] さんは退職" "済みとなります。\n\n" "{cta_line}\n\n— Helix IT プラットフォーム" ), "card_button_label": "去执行", "card_button_label_en": "Execute override", "card_button_label_ja": "実行する", }, # override_service.py::_notify_primary_rejected "leaver_blocked.dual_sign.rejected": { "subject": "[副签已驳回] {leaver_name} 的离职 override 未通过", "subject_en": ( "[Rejected] The offboarding override for {leaver_name} was not " "approved" ), "subject_ja": ( "[副署却下] {leaver_name} さんの退職オーバーライドは承認されませんでした" ), "body_text": ( "副签人 [b]{secondary_signer_name}[/b] 驳回了该 override。\n\n" "驳回理由:\n{reason}\n\n" "如仍需 override,请重新发起并补充材料。\n\n" "{cta_line}\n\n— Helix IT 平台" ), "body_text_en": ( "Co-signer [b]{secondary_signer_name}[/b] rejected the override.\n\n" "Reason given:\n{reason}\n\n" "If the override is still needed, raise it again with additional " "justification.\n\n" "{cta_line}\n\n— Helix IT Platform" ), "body_text_ja": ( "副署者 [b]{secondary_signer_name}[/b] さんがオーバーライドを却下し" "ました。\n\n" "却下理由:\n{reason}\n\n" "引き続きオーバーライドが必要な場合は、補足資料を添えて再申請して" "ください。\n\n" "{cta_line}\n\n— Helix IT プラットフォーム" ), }, # ── 入职 ──────────────────────────────────────────────────── # hire_day_service.py::_send_day90_survey —— 语言分支已由共享的 # locale_is_en(person) 决定(生产库里 SF 自由文本 "chinese"/"中文" 用 # startswith("zh") 会被误判成非中文)。模板按 resolve_lang 选语言,与 # locale_is_en 同源,zh/en 的文案与线上保持一致,ja 为新增。 # 注意:卡片的 stat / section 和邮件 HTML 仍由代码按 is_en 组装,日文 # 收件人会看到日文标题正文 + 英文小字模块 —— 这是有意的部分本地化, # 完全日化需要改 _build_day90_survey_fields。 # context 本次新增:person_name / company_name。 "onboarding.day90_survey": { "subject": "📝 入职 {company_name} 90 天啦,想听听你的感受", "subject_en": "📝 90 days at {company_name} — how has it been?", "subject_ja": "📝 {company_name} にご入社から 90 日 —— ご感想を聞かせてください", "body_text": ( "{person_name},不知不觉你已加入 {company_name} [b]90 天[/b]了!" "入职体验如何,只有你能告诉我们 —— 花几分钟聊聊,让之后加入的 " "PingCAPer 走得更顺 🙌\n\n" "问卷回复完全保密,仅用于内部改进。感谢你的宝贵意见!💙\n\n" "{cta_line}" ), "body_text_en": ( "{person_name}, just like that, you've been with {company_name} for " "[b]90 days[/b]! Only you can tell us what onboarding really felt " "like — a few minutes of your feedback makes it smoother for every " "PingCAPer who joins after you 🙌\n\n" "Responses are confidential and used for internal improvement only. " "Thank you for your valuable input! 💙\n\n" "{cta_line}" ), "body_text_ja": ( "{person_name} さん、{company_name} にご入社されてから [b]90 日[/b] が" "経ちました!入社後の体験がどうだったかは、ご本人にしか分かりません。" "数分のご協力が、これから入社する PingCAPer のためになります 🙌\n\n" "回答は完全に非公開で、社内の改善のみに利用します。ご協力ありがとう" "ございます 💙\n\n" "{cta_line}" ), "card_button_label": "填写问卷 →", "card_button_label_en": "Fill in the survey →", "card_button_label_ja": "アンケートに回答する →", }, # ── 平台日常 ──────────────────────────────────────────────── # digest_service.py::send_daily_digest —— {body} 是调用点用中文拼好的明细 # 块(近期入职 / 入职就绪 / 今日应离职 / 运行状态 / 详情入口)。模板只本地化 # 外框和概览行;明细块要完全三语化得改 digest_service 的行拼装逻辑,不在 # 本次「模板化 + 翻译」范围内。 "lifecycle_daily_digest": { "subject": "[IT 日报] {date}", "subject_en": "[IT daily digest] {date}", "subject_ja": "[IT 日報] {date}", "body_text": ( "IT 生命周期日报 —— {date}\n\n" "概览:近期入职 [b]{upcoming_hires}[/b] 人 · 今日应离职 " "[b]{today_leavers}[/b] 人 · 未解 blocker [r]{open_blockers}[/r] 项 · " "未解合规告警 [r]{open_incidents}[/r] 项\n\n" "{body}\n\n{cta_line}" ), "body_text_en": ( "Lifecycle daily digest — {date}\n\n" "At a glance — upcoming hires: [b]{upcoming_hires}[/b] · leavers " "today: [b]{today_leavers}[/b] · open blockers: " "[r]{open_blockers}[/r] · open compliance incidents: " "[r]{open_incidents}[/r]\n\n" "Detail (generated in Chinese):\n{body}\n\n{cta_line}" ), "body_text_ja": ( "ライフサイクル日報 —— {date}\n\n" "概要:入社予定 [b]{upcoming_hires}[/b] 名 · 本日退職 " "[b]{today_leavers}[/b] 名 · 未解決ブロッカー [r]{open_blockers}[/r] " "件 · 未解決のコンプライアンス警告 [r]{open_incidents}[/r] 件\n\n" "詳細(中国語で生成されています):\n{body}\n\n{cta_line}" ), }, # hr/service.py::auto_confirm_if_eligible —— 小增量批次自动确认后给触发的 # 管理员一张飞书汇总卡。context 已有 batch_id / new_count / events_staged。 "hris.batch_auto_confirmed": { "subject": "HRIS 小增量已自动确认", "subject_en": "HRIS small delta auto-confirmed", "subject_ja": "HRIS の小規模差分を自動確定しました", "body_text": ( "同步批次 {batch_id} 已自动确认:新增 [b]{new_count}[/b] 人," "0 离职 / 0 转岗,共暂存 {events_staged} 个事件。\n\n" "{cta_line}\n\n— Helix IT 平台" ), "body_text_en": ( "Sync batch {batch_id} was auto-confirmed — new hires: " "[b]{new_count}[/b], leavers: 0, role changes: 0, events staged: " "{events_staged}.\n\n" "{cta_line}\n\n— Helix IT Platform" ), "body_text_ja": ( "同期バッチ {batch_id} を自動確定しました:新規 [b]{new_count}[/b] 名、" "退職 0 名 / 異動 0 名、ステージングされたイベントは " "{events_staged} 件です。\n\n" "{cta_line}\n\n— Helix IT プラットフォーム" ), }, # post_leaver_service.py::_alert_it_admins —— context 原本把整段中文正文 # 塞进 incidents;本次补 incident_list(只含条目清单 + 溢出行),外框文案 # 交给模板,否则英文 / 日文正文里会嵌一段重复的中文导语。 # leaver_iam_residue.py::_alert_admins —— 与 compliance_incident_alert # 同源的教训:正文外框交给模板,context 只给条目清单,否则英文 / 日文 # 收件人会看到一段中文导语。 "compliance_leaver_iam_residue": { "subject": "[合规告警] {count} 名已离职人员在云上仍有 IAM 权限", "subject_en": ( "[Compliance alert] {count} departed employee(s) still hold " "cloud IAM access" ), "subject_ja": ( "[コンプライアンス警告] 退職者 {count} 名がクラウド IAM 権限を" "保持しています" ), "body_text": ( "云端 IAM 镜像与 HR 离职名单的每日对账发现 [r]{count}[/r] 条新的" "权限残留。这些人的 Helix 授权记录可能早已标记回收,但云端权限" "还在 —— 这类残留不会被任何自动清扫再次发现,需要人工确认后清理:" "\n\n{residue_list}\n\n{cta_line}\n\n— Helix IT 平台" ), "body_text_en": ( "The daily reconciliation between the cloud IAM mirror and the " "HR leaver list found [r]{count}[/r] new residual grant(s). " "Helix may have marked these revoked long ago while the " "cloud-side permission is still live — no automated sweep will " "find this again, so it needs a human decision:" "\n\n{residue_list}\n\n{cta_line}\n\n— Helix IT Platform" ), "body_text_ja": ( "クラウド IAM ミラーと HR 退職者リストの日次突合で、新たな残存" "権限を [r]{count}[/r] 件検出しました。Helix 上では回収済みでも、" "クラウド側の権限が有効なままです —— 自動クリーンアップが二度と" "検出しないため、担当者の確認が必要です:" "\n\n{residue_list}\n\n{cta_line}\n\n— Helix IT プラットフォーム" ), "card_button_label": "去处理", "card_button_label_en": "Review residue", "card_button_label_ja": "対応する", }, "compliance_incident_alert": { "subject": "[合规告警] 发现 {count} 项已离职人员残留资源", "subject_en": ( "[Compliance alert] Leftover resources found for departed " "employees: {count}" ), "subject_ja": "[コンプライアンス警告] 退職者の残存リソース {count} 件を検出", "body_text": ( "T+1 复查发现 [r]{count}[/r] 项新的已离职人员残留资源,请尽快处理:\n\n" "{incident_list}\n\n" "{cta_line}\n\n— Helix IT 平台" ), "body_text_en": ( "The T+1 re-check found new leftover resources belonging to " "departed employees — [r]{count}[/r] in total. Please clean them " "up:\n\n" "{incident_list}\n\n" "{cta_line}\n\n— Helix IT Platform" ), "body_text_ja": ( "T+1 の再点検で、退職者に紐づく残存リソースを新たに [r]{count}[/r] " "件検出しました。速やかにご対応ください:\n\n" "{incident_list}\n\n" "{cta_line}\n\n— Helix IT プラットフォーム" ), "card_button_label": "去处理", "card_button_label_en": "Open incidents", "card_button_label_ja": "対応する", }, # role_change_handlers.py::scope_review_sweep. # # 🔴 正文刻意**不写落地页的名字**(2026-09-05)。这张卡有两类收件人, # 落地页不同:scope 负责人去 ``/my/cloud/scopes/?tab=pending`` # (他打得开、也是他现在能做决定的地方),管理员去 # ``/admin/role-changes``。写死一个页面名,对另一半收件人就是错的 —— # 而在 2026-09-05 之前它写死的恰好是负责人**打不开**的那一个, # 端点也对他 403。链接由 ``{cta_line}`` 从 envelope 的 ``url`` 生成。 # # context 本次补 role_label # (role_display_name 缺失时回落 role_key,正文不能出现 None)与 stale_days # (原来是模块常量 _REVIEW_STALE_DAYS,没进 context)。 "role_change_review_30d": { "subject": "云资源授权 pending_review 超过 {stale_days} 天,请尽快决断", "subject_en": ( "Cloud grant has been pending_review for over {stale_days} days — " "please decide" ), "subject_ja": ( "クラウド権限の pending_review が {stale_days} 日を超えています。" "ご判断をお願いします" ), "body_text": ( "角色 [b]{role_label}[/b] 在 scope {scope_id} 上的持有人已转岗," "该授权挂在 pending_review 状态已超过 [r]{stale_days}[/r] 天。\n\n" "请在转岗复核页面确认[b]保留[/b]或[b]回收[/b]。\n\n" "{cta_line}\n\n— Helix IT 平台" ), "body_text_en": ( "The holder of role [b]{role_label}[/b] on scope {scope_id} has " "changed jobs, and the grant has been sitting in pending_review for " "more than [r]{stale_days}[/r] days.\n\n" "Please decide [b]keep[/b] or [b]revoke[/b].\n\n" "{cta_line}\n\n— Helix IT Platform" ), "body_text_ja": ( "スコープ {scope_id} のロール [b]{role_label}[/b] の保有者が異動した" "ため、この権限は [r]{stale_days}[/r] 日以上 pending_review のままに" "なっています。\n\n" "[b]維持[/b]または[b]回収[/b]をご判断ください。\n\n" "{cta_line}\n\n— Helix IT プラットフォーム" ), "card_button_label": "去决断", "card_button_label_en": "Review the grant", "card_button_label_ja": "判断する", }, # ── 离职数据交接(2026-08-13)──────────────────────────────── # OffboardingService._notify_handover_assigned —— 被指定为某位离职者的 # 数据交接人时通知本人。这是本人**能动手**的事(确认或改派),所以走 # action bucket(categories.py 里挂在 CAT_ASSETS 的 exact 上,和 # transfer_received 同族),而不是 offboarding. 前缀默认的 info bucket。 # context: leaver_name / leaver_email / leaver_date / assigned_reason / # receives_summary "offboarding.handover_assigned": { "subject": "[数据交接] 你是 {leaver_name} 的交接人", "subject_en": "[Handover] You are the handover person for {leaver_name}", "subject_ja": "[引き継ぎ] {leaver_name} さんの引き継ぎ担当者に指定されました", "body_text": ( "你被指定为 [b]{leaver_name}[/b]({leaver_email},离职日 " "{leaver_date})的数据交接人。\n\n" " · 指定方式: {assigned_reason}\n" " · 你将接收: {receives_summary}\n\n" "如果更合适的人选是同组的其他同事,可以在「我的交接」页面自行改派," "不需要找 IT。\n\n" "{cta_line}\n\n— Helix IT 平台" ), "body_text_en": ( "You have been named the data handover person for " "[b]{leaver_name}[/b] ({leaver_email}, last working day " "{leaver_date}).\n\n" " · How you were chosen: {assigned_reason}\n" " · You will receive: {receives_summary}\n\n" "If a teammate is the better fit, you can reassign it yourself on " "the 「My handovers」 page — no need to go through IT.\n\n" "{cta_line}\n\n— Helix IT Platform" ), "body_text_ja": ( "[b]{leaver_name}[/b]({leaver_email}、最終出社日 {leaver_date})" "のデータ引き継ぎ担当者に指定されました。\n\n" " · 指定方法: {assigned_reason}\n" " · 引き継ぐ内容: {receives_summary}\n\n" "同じチームの他の方が適任であれば、「マイ引き継ぎ」ページからご自身で" "変更できます(IT への依頼は不要です)。\n\n" "{cta_line}\n\n— Helix IT プラットフォーム" ), "card_button_label": "查看我的交接", "card_button_label_en": "View my handovers", "card_button_label_ja": "マイ引き継ぎを見る", }, # OffboardingService.reassign_my_handover —— 交接人把自己名下的交接改派 # 给别人时,原交接人收到的确认(新交接人收到上面那条 assigned)。 # context: leaver_name / new_owner_name / new_owner_email "offboarding.handover_reassigned_ack": { "subject": "[数据交接] 已改派 {leaver_name} 的交接给 {new_owner_name}", "subject_en": "[Handover] {leaver_name}'s handover reassigned to {new_owner_name}", "subject_ja": "[引き継ぎ] {leaver_name} さんの引き継ぎを {new_owner_name} さんへ変更しました", "body_text": ( "你已把 [b]{leaver_name}[/b] 的数据交接改派给 [b]{new_owner_name}[/b]" "({new_owner_email}),对方已收到通知。\n\n" "如果这是误操作,可以在「我的交接」页面找回并再次改派。\n\n" "— Helix IT 平台" ), "body_text_en": ( "You reassigned [b]{leaver_name}[/b]'s data handover to " "[b]{new_owner_name}[/b] ({new_owner_email}); they have been " "notified.\n\n" "If this was a mistake, you can find it again on the 「My " "handovers」 page and reassign.\n\n" "— Helix IT Platform" ), "body_text_ja": ( "[b]{leaver_name}[/b] さんのデータ引き継ぎを [b]{new_owner_name}[/b] " "さん({new_owner_email})へ変更しました。相手には通知済みです。\n\n" "誤操作の場合は「マイ引き継ぎ」ページから再度変更できます。\n\n" "— Helix IT プラットフォーム" ), }, # ── 删号闸门 / 交接缺口的 IT 告警(2026-08-17 补模板)────────── # 以下四条都是 IT-only 的运维告警,收件人是 ``_active_admin_recipients()`` # 的 global_admin / it_admin —— 其中有日本同事,所以同样三语。 # # 明细行(人名 / 邮箱 / 日期 / 坐席名)由调用点拼成**语言中立**的块再放进 # context,与 ``jira.*`` 的 ``sample_block`` 同一套做法:块里只有编码化的 # 事实,散文全部由模板承载,这样日语卡片里不会夹一段中文。唯一的例外是 # ``offboarding_delete_preview`` 的就绪状态(「无交接人」/「转移已完成」), # 那是三选一的枚举文案而不是数据,所以调用点按 ``_zh`` / ``_en`` / ``_ja`` # 各拼一份列表(locale fragment,见 parity 测试的 ``_LOCALE_SUFFIXES``)。 # # OffboardingService._notify_delete_blocked —— 终态删号被拒:数据交接人 # 解析不出来,或 Drive/日历转移连续失败。每个流程每种 reminder_kind 只发 # 一次。context: leaver_name / leaver_email / channel(产品名,语言中立)/ # reason_zh / reason_en / reason_ja。 "offboarding_delete_blocked_no_successor": { "subject": "[离职删号已暂停] {leaver_name} 的 {channel} 账号缺少数据交接人", "subject_en": ( "[Delete paused] No data handover person for {leaver_name}'s " "{channel} account" ), "subject_ja": ( "[削除保留] {leaver_name} さんの {channel} アカウントに" "データ引き継ぎ担当者がいません" ), "body_text": ( "[b]{leaver_email}[/b] 的 {channel} 账号已到硬删除时间,但未能确认" "数据交接({reason_zh})。\n\n" "为避免 Drive / 日历 / 云录制内容被永久删除,本次删除已暂停,系统会在" "补齐交接人后自动重试。\n\n" "请在离职流程详情页设置「默认交接人」。\n\n" "{cta_line}\n\n— Helix IT 平台" ), "body_text_en": ( "[b]{leaver_email}[/b]'s {channel} account has reached its hard-delete " "date, but the data handover could not be confirmed " "({reason_en}).\n\n" "The delete is paused so that Drive / calendar / cloud recordings are " "not destroyed for good; it retries automatically once a handover " "person exists.\n\n" "Please set the default handover person on the offboarding process " "page.\n\n" "{cta_line}\n\n— Helix IT Platform" ), "body_text_ja": ( "[b]{leaver_email}[/b] さんの {channel} アカウントは完全削除の予定日を" "迎えましたが、データの引き継ぎを確認できませんでした" "({reason_ja})。\n\n" "Drive・カレンダー・クラウド録画が失われるのを防ぐため、今回の削除は" "保留しました。引き継ぎ担当者が設定されると自動的に再実行されます。\n\n" "退職プロセスの詳細ページで「デフォルト引き継ぎ担当者」を設定して" "ください。\n\n" "{cta_line}\n\n— Helix IT プラットフォーム" ), "card_button_label": "查看离职流程", "card_button_label_en": "Open offboarding process", "card_button_label_ja": "退職プロセスを開く", }, # OffboardingService._notify_handover_unresolved —— 对账扫描发现的「交接人 # 解析不出来」汇总,每个流程一生只进一次汇总。 # context: count / leaver_list(语言中立: ``· 姓名 (邮箱) — ISO 离职日``)。 "offboarding_handover_unresolved": { "subject": "[离职交接] {count} 位离职者缺少数据交接人", "subject_en": "[Handover] {count} leavers have no data handover person", "subject_ja": "[引き継ぎ] {count} 名の退職者に引き継ぎ担当者がいません", "body_text": ( "以下 [b]{count}[/b] 位离职者没有可用的数据交接人(默认交接人为空," "上级链上也没有在职的人):\n\n" "{leaver_list}\n\n" "在补齐交接人之前,他们的 Google / Zoom 账号不会被硬删除(数据不会丢失," "但账号会一直停在已停用状态)。\n" "请在离职流程详情页设置「默认交接人」,或在平台参数里配置兜底交接人 " "offboarding.default_handover_fallback_person_id。\n\n" "{cta_line}\n\n— Helix IT 平台" ), "body_text_en": ( "[b]{count}[/b] leavers have no usable data handover person (no " "default handover person, and nobody on their manager chain is still " "employed):\n\n" "{leaver_list}\n\n" "Until one is named their Google / Zoom accounts are never " "hard-deleted — no data is lost, but the accounts stay suspended " "forever.\n" "Set the default handover person on each offboarding process, or " "configure the platform-wide fallback " "offboarding.default_handover_fallback_person_id.\n\n" "{cta_line}\n\n— Helix IT Platform" ), "body_text_ja": ( "以下の [b]{count}[/b] 名の退職者には、利用可能なデータ引き継ぎ担当者が" "いません(デフォルト担当者が未設定で、上長チェーンにも在職者が" "いません):\n\n" "{leaver_list}\n\n" "担当者が設定されるまで、これらの Google / Zoom アカウントは完全削除" "されません(データは失われませんが、アカウントは停止状態のままに" "なります)。\n" "退職プロセスの詳細ページで「デフォルト引き継ぎ担当者」を設定するか、" "プラットフォーム設定で " "offboarding.default_handover_fallback_person_id を指定してください。" "\n\n{cta_line}\n\n— Helix IT プラットフォーム" ), "card_button_label": "查看离职进度", "card_button_label_en": "Open offboarding progress", "card_button_label_ja": "退職の進捗を開く", }, # OffboardingService._notify_delete_preview —— 明天要硬删的账号预告(T-1), # 给 IT 一个「反悔窗口」。 # context: count / account_list_zh / account_list_en / account_list_ja # (就绪状态是枚举文案,所以三份;人名 / 邮箱 / 渠道名都相同)。 "offboarding_delete_preview": { "subject": "[离职删号预告] 明天将删除 {count} 个账号", "subject_en": "[Delete preview] {count} leaver accounts are deleted tomorrow", "subject_ja": "[削除予告] 明日 {count} 件の退職者アカウントを削除します", "body_text": ( "明天将硬删除以下 [b]{count}[/b] 个离职账号:\n\n" "{account_list_zh}\n\n" "[r]⚠️[/r] Gmail 邮件内容无法转移 —— Google 的 Data Transfer 只搬 Drive " "和日历,邮箱里的内容会随账号一起消失。若某个邮箱必须保留,请在明天之前" "把它在「外部账号登记」里标为保留(shared_service / external_guest / " "contractor),标了就永不删除。\n" "[r]⚠️[/r] Zoom 删除不可恢复;Google 删除后约 20 天内可在 Google " "管理控制台 undelete。\n\n" "{cta_line}\n\n— Helix IT 平台" ), "body_text_en": ( "These [b]{count}[/b] leaver accounts are hard-deleted tomorrow:\n\n" "{account_list_en}\n\n" "[r]⚠️[/r] Gmail content cannot be transferred — Google's Data " "Transfer moves Drive and calendar only, so everything in the mailbox " "goes with the account. If a mailbox must be kept, mark it as retained " "in the external account registry before tomorrow (shared_service / " "external_guest / contractor); anything marked is never deleted.\n" "[r]⚠️[/r] Zoom deletion is irreversible; a deleted Google account can " "be undeleted in the Google Admin console for about 20 days.\n\n" "{cta_line}\n\n— Helix IT Platform" ), "body_text_ja": ( "明日、以下の [b]{count}[/b] 件の退職者アカウントを完全削除します:\n\n" "{account_list_ja}\n\n" "[r]⚠️[/r] Gmail の中身は移管できません —— Google の Data Transfer が" "移せるのは Drive とカレンダーだけで、メールボックスの内容は" "アカウントとともに消えます。残す必要があるメールボックスは、明日までに" "「外部アカウント登録」で保持対象(shared_service / external_guest / " "contractor)に設定してください。設定したものは削除されません。\n" "[r]⚠️[/r] Zoom の削除は元に戻せません。Google は削除後およそ 20 日間、" "管理コンソールから復元できます。\n\n" "{cta_line}\n\n— Helix IT プラットフォーム" ), "card_button_label": "查看离职进度", "card_button_label_en": "Open offboarding progress", "card_button_label_ja": "退職の進捗を開く", }, # OffboardingService._notify_stale_handover_blockers —— 重扫时消失的坐席里 # 有「本该搬数据」的那种(Google / Zoom),提醒 IT 确认不是误回收。 # context: leaver_name / leaver_email / blocker_list(坐席名,语言中立)。 "offboarding_stale_handover_blocker": { "subject": "[离职交接] {leaver_name} 的数据交接坐席在重扫时消失", "subject_en": ( "[Handover] Data-moving seats for {leaver_name} vanished on rescan" ), "subject_ja": ( "[引き継ぎ] {leaver_name} さんのデータ移管対象シートが再スキャンで" "消えました" ), "body_text": ( "[b]{leaver_name}[/b]({leaver_email})的离职流程里,以下坐席在重新扫描时" "已经不在了,被自动跳过:\n\n" "{blocker_list}\n\n" "这些坐席的回收动作原本会[b]转移数据[/b](Google Drive / 日历,或 Zoom " "会议 / 云录制),跳过意味着那次转移没有发生。\n" "删号闸门会在最终删除前兜底重做一次转移,所以数据不会丢;但如果这些坐席" "是被误回收的,请确认一下。\n\n" "{cta_line}\n\n— Helix IT 平台" ), "body_text_en": ( "On [b]{leaver_name}[/b]'s ({leaver_email}) offboarding process the " "seats below were gone when the scan re-ran, so they were skipped " "automatically:\n\n" "{blocker_list}\n\n" "Revoking these seats would have [b]moved data[/b] (Google Drive / " "calendar, or Zoom meetings / cloud recordings), so skipping them means " "that transfer never happened.\n" "The delete gate redoes the transfer before the final deletion, so no " "data is lost — but if these seats were revoked by mistake, please " "check.\n\n" "{cta_line}\n\n— Helix IT Platform" ), "body_text_ja": ( "[b]{leaver_name}[/b] さん({leaver_email})の退職プロセスで、以下の" "シートが再スキャン時に存在せず、自動的にスキップされました:\n\n" "{blocker_list}\n\n" "これらのシートの回収処理は本来[b]データを移管する[/b]もの" "(Google Drive・カレンダー、または Zoom のミーティング・クラウド録画)" "で、スキップされたということはその移管が行われていません。\n" "削除ゲートが最終削除の前に移管をやり直すためデータは失われませんが、" "これらのシートが誤って回収されたものでないかご確認ください。\n\n" "{cta_line}\n\n— Helix IT プラットフォーム" ), "card_button_label": "查看离职流程", "card_button_label_en": "Open offboarding process", "card_button_label_ja": "退職プロセスを開く", }, # ── 入职 IT 就绪度升级(仅发 IT global_admin,打扰预算红线) ──────── # ``onboarding/service.py::_dispatch_escalation`` 用 # f"onboarding.{kind}" 拼 key,三个 kind 必须都注册 —— 在此之前整族 # 都没有模板,收件的 IT 管理员无论读什么语言都只拿到中文。 # context: hire_display_name, hire_date, days_past, pending_count, # sla_breach_level, gap_lines(三语 LocalizedText) "onboarding.day0_it_gaps": { "subject": "🚨 {hire_display_name} 今天入职,有 {pending_count} 项 IT 工作没完成", "subject_en": "🚨 {hire_display_name} starts today — {pending_count} IT item(s) still open", "subject_ja": "🚨 {hire_display_name} さんは本日入社 — IT 対応が {pending_count} 件未完了", "body_text": ( "{hire_display_name}(今天入职,入职日 {hire_date})\n\n" "以下工作需要 IT 完成,该员工才能正常开工:\n" "{gap_lines}\n\n" "👉 打开该员工的入职详情,逐项分配 / 标记已发放;" "确实不需要的可「跳过」并填写原因。\n\n" "{cta_line}\n\n" "— Helix IT 平台" ), "body_text_en": ( "{hire_display_name} (starts today, hire date {hire_date})\n\n" "IT still has to finish the following before this person can " "start working:\n" "{gap_lines}\n\n" "👉 Open the hire's onboarding detail and assign / mark each item " "as handed out; anything genuinely not needed can be skipped with " "a reason.\n\n" "{cta_line}\n\n" "— Helix IT Platform" ), "body_text_ja": ( "{hire_display_name} さん(本日入社、入社日 {hire_date})\n\n" "この方が業務を開始するには、IT 側で以下の対応が必要です:\n" "{gap_lines}\n\n" "👉 入社詳細を開き、各項目の割り当て / 配布済みの記録をお願いします。" "不要な項目は理由を添えて「スキップ」できます。\n\n" "{cta_line}\n\n" "— Helix IT プラットフォーム" ), "card_button_label": "去 Helix 查看", "card_button_label_en": "Open in Helix", "card_button_label_ja": "Helix で確認する", }, "onboarding.sla_urgent": { "subject": "⚠️ {hire_display_name} 已入职 {days_past} 天,还有 {pending_count} 项 IT 工作没完成", "subject_en": "⚠️ {hire_display_name} started {days_past} day(s) ago — {pending_count} IT item(s) still open", "subject_ja": "⚠️ {hire_display_name} さんの入社から {days_past} 日 — IT 対応が {pending_count} 件未完了", "body_text": ( "{hire_display_name}(已入职 {days_past} 天,入职日 {hire_date})\n\n" "以下工作需要 IT 完成,该员工才能正常开工:\n" "{gap_lines}\n\n" "👉 打开该员工的入职详情,逐项分配 / 标记已发放;" "确实不需要的可「跳过」并填写原因。\n\n" "{cta_line}\n\n" "— Helix IT 平台" ), "body_text_en": ( "{hire_display_name} (started {days_past} day(s) ago, hire date {hire_date})\n\n" "IT still has to finish the following before this person can " "start working:\n" "{gap_lines}\n\n" "👉 Open the hire's onboarding detail and assign / mark each item " "as handed out; anything genuinely not needed can be skipped with " "a reason.\n\n" "{cta_line}\n\n" "— Helix IT Platform" ), "body_text_ja": ( "{hire_display_name} さん(入社から {days_past} 日、入社日 {hire_date})\n\n" "この方が業務を開始するには、IT 側で以下の対応が必要です:\n" "{gap_lines}\n\n" "👉 入社詳細を開き、各項目の割り当て / 配布済みの記録をお願いします。" "不要な項目は理由を添えて「スキップ」できます。\n\n" "{cta_line}\n\n" "— Helix IT プラットフォーム" ), "card_button_label": "去 Helix 查看", "card_button_label_en": "Open in Helix", "card_button_label_ja": "Helix で確認する", }, "onboarding.sla_breach": { "subject": "🚨 {hire_display_name} 已入职 {days_past} 天仍未配齐,已超时 —— 请立即处理", "subject_en": "🚨 {hire_display_name} started {days_past} day(s) ago and is still not set up — SLA breached", "subject_ja": "🚨 {hire_display_name} さんの入社から {days_past} 日、いまだ未整備 — SLA 超過", "body_text": ( "{hire_display_name}(已入职 {days_past} 天,入职日 {hire_date})\n\n" "以下工作需要 IT 完成,该员工才能正常开工:\n" "{gap_lines}\n\n" "👉 打开该员工的入职详情,逐项分配 / 标记已发放;" "确实不需要的可「跳过」并填写原因。\n\n" "{cta_line}\n\n" "— Helix IT 平台" ), "body_text_en": ( "{hire_display_name} (started {days_past} day(s) ago, hire date {hire_date})\n\n" "IT still has to finish the following before this person can " "start working:\n" "{gap_lines}\n\n" "👉 Open the hire's onboarding detail and assign / mark each item " "as handed out; anything genuinely not needed can be skipped with " "a reason.\n\n" "{cta_line}\n\n" "— Helix IT Platform" ), "body_text_ja": ( "{hire_display_name} さん(入社から {days_past} 日、入社日 {hire_date})\n\n" "この方が業務を開始するには、IT 側で以下の対応が必要です:\n" "{gap_lines}\n\n" "👉 入社詳細を開き、各項目の割り当て / 配布済みの記録をお願いします。" "不要な項目は理由を添えて「スキップ」できます。\n\n" "{cta_line}\n\n" "— Helix IT プラットフォーム" ), "card_button_label": "去 Helix 查看", "card_button_label_en": "Open in Helix", "card_button_label_ja": "Helix で確認する", }, # role_change_handlers.py::review_notify (migration 568). The card # goes to the NEW MANAGER — an ordinary employee, not IT — because # they are the only person who can answer "does this colleague still # need this". Deliberately a SUMMARY: the real packets run to # hundreds of items across ten categories, and a card that listed # them would be the review storm this feature exists to avoid. "role_change_review_open": { "subject": "{person_name} 转岗到你团队 —— {reviewable} 项权限待你确认", "subject_en": ( "{person_name} has moved into your team — {reviewable} items " "need your call" ), "subject_ja": ( "{person_name} さんがあなたのチームに異動 —— " "{reviewable} 件の権限確認をお願いします" ), "body_text": ( "[b]{person_name}[/b] 已从 [b]{before_dept}[/b] 转岗到 " "[b]{after_dept}[/b]。\n\n" "Ta 名下现在有 [r]{reviewable}[/r] 项权限和归属需要有人判断是" "「继续保留」还是「不再需要」——这些东西不会自己失效," "而你是最清楚 Ta 接下来要做什么的人。\n\n" "涉及 {category_count} 类:{category_summary}\n\n" "{ai_line}" "可以按类别整批处理,不必逐条点。Helix 不会替你动任何权限," "你的选择只是记录下来,由 IT 按记录执行。\n\n" "{cta_line}\n\n— Helix IT 平台" ), "body_text_en": ( "[b]{person_name}[/b] has moved from [b]{before_dept}[/b] to " "[b]{after_dept}[/b].\n\n" "They currently hold [r]{reviewable}[/r] entitlements and " "ownerships that somebody needs to judge as still-needed or " "not — none of these lapse on their own, and you are the " "person who knows what they will be working on.\n\n" "Across {category_count} categories: {category_summary}\n\n" "{ai_line}" "You can answer a whole category at once. Helix changes no " "access on its own — your answers are recorded, and IT acts " "on the record.\n\n" "{cta_line}\n\n— Helix IT Platform" ), "body_text_ja": ( "[b]{person_name}[/b] さんが [b]{before_dept}[/b] から " "[b]{after_dept}[/b] へ異動しました。\n\n" "現在 [r]{reviewable}[/r] 件の権限・所有関係について、" "引き続き必要かどうかの判断が必要です。これらは自動では失効せず、" "今後の担当業務を一番よくご存じなのはあなたです。\n\n" "対象は {category_count} カテゴリ: {category_summary}\n\n" "{ai_line}" "カテゴリ単位でまとめて回答できます。Helix が権限を勝手に" "変更することはありません。回答は記録され、IT が記録に沿って" "対応します。\n\n" "{cta_line}\n\n— Helix IT プラットフォーム" ), "card_button_label": "去确认", "card_button_label_en": "Review now", "card_button_label_ja": "確認する", }, # ── HR《离职流程指引》告知信 ──────────────────────────────── # hr_lifecycle/pending_leaver_notice_service.py —— SF 离职审批批完的那一刻 # 推给本人。正文逐字来自 HR 的飞书文档《Offboarding Notification》 # (wiki HnQNwxfyoiHzjZkRVt3cUnTdnEa,2026-08-31 版),两个雇佣类型两份文案。 # 改这里的中文 = 改员工收到的信,动之前先和 HR 对一遍。 # # ``{beijing_section}`` 由调用点传 ``LocalizedText``:北京员工是那段 # 「人事档案 / 工作居住证 / 党组织关系」,其他城市是空串 —— HR 文档里 # 「仅北京员工推送」的那一条。空串在这里是正确的渲染结果,不是缺 context。 "offboarding.hr_notice_regular": { "subject": "[离职流程] 您的离职审批已完成 — 最后工作日 {last_working_day}", "subject_en": ( "[Offboarding] Your resignation is approved — " "last working day {last_working_day}" ), "subject_ja": ( "[退職手続き] 退職の承認が完了しました — " "最終出社日 {last_working_day}" ), "body_text": ( "亲爱的小伙伴,感谢一路相伴!\n\n" "您的离职审批已完成,最后工作日为 [b]{last_working_day}[/b]。" "交接期间及离职后请注意履行相应保密义务。\n\n" "请在最后工作日前完成如下交接事项:\n\n" "1. 工作及资料交接\n" "梳理您所负责的工作、待办、项目与客户信息,并与工作交接人完成移交;" "全部电子 / 纸质文件、合同、技术资料归档移交至工作交接人,删除个人" "设备内涉密公司数据;移交完毕后,请同步直属 Leader。\n\n" "2. 公司资产归还\n" "电脑等办公资产归还请登录 [【Helix】](https://helix.pingcap.net/my/requests) " "系统走对应的申请,操作详情可在 IT Help Desk 查询;" "办公用品、门禁卡、工牌交还各地 HROP。\n\n" "3. 账号权限与群组处理\n" "飞书、邮箱、业务系统等账号将于最后工作日 24:00 统一关停,并同时解绑" "手机号、身份证等实名认证信息;请提前退出所有与公司及业务相关的群组," "如为群主 / 管理员,请务必提前完成身份转让。\n\n" "4. 报销\n" "· 报销单据请在离职交接期内提交并完成审批。\n\n" "5. 薪资结算\n" "· 离职当月 20 日及之前的薪资,当月最后一个工作日发放;离职当月 21 日" "及之后的薪资,次月最后一个工作日发放,具体工资及餐补数额根据实际" "出勤结果进行结算;如涉及其他扣款或补偿部分的结算,以与 HRBP 及 C&B " "沟通确认为准。\n" "· 工资条可通过 " "[SUNSHINE 系统](https://hris.app.unifyhrs.com/login/PINGCAP) 查询:每月工资单于" "发薪当日 0 时向员工开放,系统登录信息已通过邮件发送到你的工作邮箱," "请在离职前登陆 gmail 邮箱搜索「PingCAP: Payslip guideline」即可找到," "并妥善保存工资条密码和系统登录密码。SUNSHINE 系统将为离职员工开放自" "离职月之后 90 天的登录权限,请及时下载和保存工资条。\n\n" "6. 年假\n" "如存在未休年假,请与直属 Leader 和 HRBP 沟通后妥善安排;如因个人" "原因在离职前未休完剩余年假,视为自动放弃。如涉及年假超休,超休" "部分将按规定进行离职结算扣回。\n\n" "7. 社保公积金 & 福利\n" "· 社保公积金的减员停缴的具体时间表,请咨询当地的 HROP;\n" "· 补充医疗自员工离职当日起终止;自费补充医疗中途离职则保险失效," "可按照未保障天数退回保费,发生理赔不退费,福利结算以实际离职日期" "为准。\n" "{beijing_section}\n" "✅ 手续办结提示:全部交接完成后,HROP 将于最后工作日当日线上推送" "《离职证明》+《离职后义务告知书》,请留意短信,当日完成签署并及时" "下载,避免文件超时失效。\n\n" "🔔 请遵守《离职后义务告知书》的相关约定,后续背调需求可发送至 " "hr@pingcap.com 或联系 HRBP。\n\n" "若对离职流程有任何问题,请咨询 HR Helpdesk。\n\n" "PingCAP HR Team" ), "body_text_en": ( "Dear colleague, thank you for everything you brought to the " "journey!\n\n" "Your resignation has been approved. Your last working day is " "[b]{last_working_day}[/b]. Please continue to honour your " "confidentiality obligations during the handover and after you " "leave.\n\n" "Please complete the following before your last working day:\n\n" "1. Work and document handover\n" "Go through the work, open items, projects and customer " "information you own and hand them over to your designated " "successor; archive and transfer all electronic / paper " "documents, contracts and technical materials to them, and " "delete confidential company data from your personal devices. " "Let your direct leader know once the handover is done.\n\n" "2. Returning company assets\n" "For laptops and other office assets, raise the matching request " "in [Helix](https://helix.pingcap.net/my/requests); the IT Help " "Desk has the step-by-step guide. Office supplies, access cards " "and badges go back to your local HROP.\n\n" "3. Accounts, access and groups\n" "Feishu, email and business-system accounts are all closed at " "24:00 on your last working day, and the real-name details bound " "to them (mobile number, ID document) are unbound at the same " "time. Please leave every company- and business-related group " "beforehand, and if you are an owner / admin of one, transfer " "that role in advance.\n\n" "4. Expense claims\n" "· Please submit your expense claims — and get them approved — " "within the handover period.\n\n" "5. Final pay\n" "· Pay for a departure on or before the 20th of the month is " "released on the last working day of that month; for a departure " "on or after the 21st it is released on the last working day of " "the following month. Salary and meal-allowance amounts are " "settled against actual attendance; any deductions or " "compensation are as confirmed with your HRBP and C&B.\n" "· Payslips are available in " "[SUNSHINE](https://hris.app.unifyhrs.com/login/PINGCAP): each month's " "payslip opens at 00:00 on pay day, and the login details were " "emailed to your work mailbox. Please sign in to Gmail BEFORE you " "leave, search for \"PingCAP: Payslip guideline\", and keep both " "the payslip password and the login password somewhere safe. " "SUNSHINE keeps leavers signed-in for 90 days after their " "departure month, so please download and keep your payslips in " "time.\n\n" "6. Annual leave\n" "If you have untaken annual leave, please agree a plan with your " "direct leader and your HRBP. Any remaining annual leave you do " "not take before you leave, for personal reasons, is treated as " "waived. Any leave taken beyond your entitlement will be " "recovered in the final settlement as per policy.\n\n" "7. Social insurance, housing fund & benefits\n" "· For the exact timing of social-insurance and housing-fund " "deregistration, please ask your local HROP.\n" "· Supplementary medical cover ends on your last working day; " "self-funded supplementary cover lapses on departure and the " "premium for the uncovered days can be refunded, except where a " "claim has been made. Benefit settlement follows your actual " "departure date.\n" "{beijing_section}\n" "✅ Completion note: once the handover is finished, HROP sends " "your Certificate of Termination and Post-Employment Obligations " "Notice online on your last working day. Watch for the SMS, sign " "the same day and download the documents promptly so they do not " "expire.\n\n" "🔔 Please observe the Post-Employment Obligations Notice. For " "future background-check requests, write to hr@pingcap.com or " "contact your HRBP.\n\n" "If you have any questions about the offboarding process, please " "reach out to the HR Helpdesk.\n\n" "PingCAP HR Team" ), "body_text_ja": ( "これまで一緒に歩んでくださり、ありがとうございました。\n\n" "退職の承認が完了しました。最終出社日は [b]{last_working_day}[/b] " "です。引き継ぎ期間中および退職後も、秘密保持義務の履行にご留意" "ください。\n\n" "最終出社日までに、以下の引き継ぎをお願いします。\n\n" "1. 業務・資料の引き継ぎ\n" "担当業務、未処理のタスク、プロジェクト、顧客情報を整理し、" "引き継ぎ担当者へお渡しください。電子・紙の書類、契約書、技術資料は" "すべてアーカイブのうえ引き継ぎ、個人デバイス内の機密データは削除して" "ください。完了後、直属の上長へご共有ください。\n\n" "2. 会社資産の返却\n" "PC などのオフィス資産は " "[【Helix】](https://helix.pingcap.net/my/requests) から該当の申請をご提出" "ください。手順は IT Help Desk で確認できます。事務用品・入館カード・" "社員証は各拠点の HROP へご返却ください。\n\n" "3. アカウント・権限・グループの整理\n" "Feishu、メール、業務システムなどのアカウントは最終出社日の 24:00 に" "一斉停止され、携帯番号・本人確認書類などの実名認証情報も同時に解除" "されます。会社・業務に関連するグループからは事前にご退出ください。" "グループのオーナー / 管理者の場合は、必ず事前に権限の移譲をお願い" "します。\n\n" "4. 経費精算\n" "· 経費の申請は引き継ぎ期間中にご提出のうえ、承認まで完了させて" "ください。\n\n" "5. 給与の精算\n" "· 退職月の 20 日以前の給与は当月最終営業日に、21 日以降の給与は翌月" "最終営業日に支給されます。給与および食事補助の金額は実際の勤怠に" "基づいて精算され、その他の控除・補償の精算は HRBP および C&B との" "確認内容に従います。\n" "· 給与明細は " "[SUNSHINE](https://hris.app.unifyhrs.com/login/PINGCAP) で確認できます。" "毎月の明細は支給日の 0 時に公開され、ログイン情報は勤務先メール宛に" "送付済みです。退職前に Gmail にログインし「PingCAP: Payslip guideline」" "で検索のうえ、明細のパスワードとログインパスワードを大切に保管して" "ください。SUNSHINE は退職月の翌月から 90 日間ログインできますので、" "お早めにダウンロード・保存してください。\n\n" "6. 年次有給休暇\n" "未消化の年次有給休暇がある場合は、直属の上長および HRBP と" "ご相談のうえ調整してください。ご本人の都合により退職前に消化" "されなかった残余の年次有給休暇は、放棄されたものとして取り扱い" "ます。付与日数を超えて取得している場合、超過分は規定に従い退職" "精算時に控除されます。\n\n" "7. 社会保険・住宅積立金・福利厚生\n" "· 社会保険と住宅積立金の資格喪失・納付停止の時期は、各拠点の HROP へ" "お問い合わせください。\n" "· 補足医療保険は退職日をもって終了します。" "自費加入の補足医療保険は退職時点で失効し、未保障期間分の保険料は" "返還されますが、保険金の請求があった場合は返還されません。" "福利厚生の精算は実際の退職日を基準とします。\n" "{beijing_section}\n" "✅ 手続完了のご案内:引き継ぎがすべて完了すると、最終出社日当日に " "HROP から《離職証明書》および《退職後の義務に関する通知書》が" "オンラインで送付されます。SMS にご注意のうえ、当日中に署名し、" "期限切れとならないよう速やかにダウンロードしてください。\n\n" "🔔 《退職後の義務に関する通知書》の内容を遵守してください。今後の" "リファレンスチェックのご依頼は hr@pingcap.com、または HRBP まで" "ご連絡ください。\n\n" "退職手続きについてご不明な点があれば、HR Helpdesk までお問い合わせ" "ください。\n\n" "PingCAP HR Team" ), "card_button_label": "去 Helix 办理归还 / 回购", "card_button_label_en": "Return or buy back in Helix", "card_button_label_ja": "Helix で返却・買い取り手続き", }, "offboarding.hr_notice_intern": { "subject": "[离职流程] 您的离职审批已完成 — 最后工作日 {last_working_day}", "subject_en": ( "[Offboarding] Your internship end is confirmed — " "last working day {last_working_day}" ), "subject_ja": ( "[退職手続き] 退職の承認が完了しました — " "最終出社日 {last_working_day}" ), "body_text": ( "亲爱的小伙伴,感谢一路相伴!\n\n" "您的离职审批已完成,最后工作日为 [b]{last_working_day}[/b]。" "交接期间及离职后请注意履行相应保密义务。\n\n" "请在最后工作日前完成如下交接事项:\n\n" "1. 工作及资料交接\n" "梳理您所负责的工作、待办、项目与客户信息,并与工作交接人完成移交;" "全部电子 / 纸质文件、合同、技术资料归档移交至工作交接人,删除个人" "设备内涉密公司数据;移交完毕后,请同步直属 Leader。\n\n" "2. 公司资产归还\n" "电脑等办公资产归还请登录 [【Helix】](https://helix.pingcap.net/my/requests) " "系统走对应的申请,操作详情可在 IT Help Desk 查询;" "办公用品、门禁卡、工牌交还各地 HROP。\n\n" "3. 账号权限与群组处理\n" "飞书、邮箱、业务系统等账号将于最后工作日 24:00 统一关停,并同时解绑" "手机号、身份证等实名认证信息;请提前退出所有与公司及业务相关的群组," "如为群主 / 管理员,请务必提前完成身份转让。\n\n" "4. 报销与薪资结算\n" "· 报销单据请在离职交接期内提交;\n" "· 当月 20 日(含)前的实习津贴,当月底发放;当月 20 日后的实习津贴," "次月底发放。\n\n" "✅ 手续办结提示:全部交接完成后,HROP 将于最后工作日当日线上推送" "《实习证明》,请留意短信,当日完成签署并及时下载,避免文件超时" "失效。\n\n" "若对离职流程有任何问题,请咨询 HR Helpdesk。\n\n" "PingCAP HR Team" ), "body_text_en": ( "Dear colleague, thank you for everything you brought to the " "journey!\n\n" "Your offboarding has been approved. Your last working day is " "[b]{last_working_day}[/b]. Please continue to honour your " "confidentiality obligations during the handover and after you " "leave.\n\n" "Please complete the following before your last working day:\n\n" "1. Work and document handover\n" "Go through the work, open items, projects and customer " "information you own and hand them over to your designated " "successor; archive and transfer all electronic / paper " "documents, contracts and technical materials to them, and " "delete confidential company data from your personal devices. " "Let your direct leader know once the handover is done.\n\n" "2. Returning company assets\n" "For laptops and other office assets, raise the matching request " "in [Helix](https://helix.pingcap.net/my/requests); the IT Help " "Desk has the step-by-step guide. Office supplies, access cards " "and badges go back to your local HROP.\n\n" "3. Accounts, access and groups\n" "Feishu, email and business-system accounts are all closed at " "24:00 on your last working day, and the real-name details bound " "to them (mobile number, ID document) are unbound at the same " "time. Please leave every company- and business-related group " "beforehand, and if you are an owner / admin of one, transfer " "that role in advance.\n\n" "4. Expense claims and final allowance\n" "· Please submit expense claims within the handover period.\n" "· An internship allowance for a departure on or before the 20th " "is paid at the end of that month; after the 20th it is paid at " "the end of the following month.\n\n" "✅ Completion note: once the handover is finished, HROP sends " "your Internship Certificate online on your last working day. " "Watch for the SMS, sign the same day and download it promptly " "so it does not expire.\n\n" "If you have any questions about the offboarding process, please " "reach out to the HR Helpdesk.\n\n" "PingCAP HR Team" ), "body_text_ja": ( "これまで一緒に歩んでくださり、ありがとうございました。\n\n" "退職の承認が完了しました。最終出社日は [b]{last_working_day}[/b] " "です。引き継ぎ期間中および退職後も、秘密保持義務の履行にご留意" "ください。\n\n" "最終出社日までに、以下の引き継ぎをお願いします。\n\n" "1. 業務・資料の引き継ぎ\n" "担当業務、未処理のタスク、プロジェクト、顧客情報を整理し、" "引き継ぎ担当者へお渡しください。電子・紙の書類、契約書、技術資料は" "すべてアーカイブのうえ引き継ぎ、個人デバイス内の機密データは削除して" "ください。完了後、直属の上長へご共有ください。\n\n" "2. 会社資産の返却\n" "PC などのオフィス資産は " "[【Helix】](https://helix.pingcap.net/my/requests) から該当の申請をご提出" "ください。手順は IT Help Desk で確認できます。事務用品・入館カード・" "社員証は各拠点の HROP へご返却ください。\n\n" "3. アカウント・権限・グループの整理\n" "Feishu、メール、業務システムなどのアカウントは最終出社日の 24:00 に" "一斉停止され、携帯番号・本人確認書類などの実名認証情報も同時に解除" "されます。会社・業務に関連するグループからは事前にご退出ください。" "グループのオーナー / 管理者の場合は、必ず事前に権限の移譲をお願い" "します。\n\n" "4. 経費精算とインターン手当\n" "· 経費の申請は引き継ぎ期間中にご提出ください。\n" "· 20 日(当日を含む)までの退職の場合、インターン手当は当月末に、" "20 日より後の場合は翌月末に支給されます。\n\n" "✅ 手続完了のご案内:引き継ぎがすべて完了すると、最終出社日当日に " "HROP から《インターンシップ証明書》がオンラインで送付されます。" "SMS にご注意のうえ、当日中に署名し、期限切れとならないよう速やかに" "ダウンロードしてください。\n\n" "退職手続きについてご不明な点があれば、HR Helpdesk までお問い合わせ" "ください。\n\n" "PingCAP HR Team" ), "card_button_label": "去 Helix 办理归还 / 回购", "card_button_label_en": "Return or buy back in Helix", "card_button_label_ja": "Helix で返却・買い取り手続き", }, # ── 邮箱名对账 ────────────────────────────────────────────── # hr_lifecycle/email_name_reconcile_service.py::_alert_admins —— IT-only。 # 只有「已经建了 Google 号 + 地址与 HR 指定的邮箱名不符」才推:没建号的那半 # 边由同一个 cron 自动重跑 reserve 修掉,不打扰人。建号之后 Helix 没有自动 # 改名能力(Google rename 要动 Directory API + 飞书 + SF + 各处 identity 行), # 所以这张卡对应的是一个真实的人工动作,不是 FYI。 # context: count / person_list(语言中立:``· 姓名 — 当前邮箱 → 应为的邮箱名@``) "hr.email_name_mismatch": { "subject": "[邮箱名不一致] {count} 位员工的邮箱与 HR 指定的不符", "subject_en": ( "[Mailbox mismatch] {count} employee(s) have a mailbox that " "disagrees with the HR-assigned email name" ), "subject_ja": ( "[メールアドレス不一致] {count} 名の実際のアドレスが HR 指定の" "メール名と一致していません" ), "body_text": ( "以下 [b]{count}[/b] 位员工的企业邮箱,与 HR 在 SuccessFactors 里" "指定的邮箱名(PerPersonal.customString4)不一致:\n\n" "{person_list}\n\n" "他们的 Google 账号[b]已经建好了[/b],所以 Helix 不会自动改 —— 建号" "之后改名要动 Google Directory、飞书、SF 回写和 Helix 侧的 identity " "行,必须有人来做。还没建号的那部分本任务已经自动纠正,不在此列。\n" "常见成因:HR 在 Helix 建人之后才补填邮箱名,而账号已经按当时的" "「名.姓」派生建好了。\n" "处理方式:Google Directory 改 primaryEmail(旧地址会自动保留为别名," "不要删号重建),再同步飞书 / SF / Helix。\n\n" "— Helix IT 平台" ), "body_text_en": ( "The corporate mailbox of the following [b]{count}[/b] " "employee(s) disagrees with the email name HR assigned in " "SuccessFactors (PerPersonal.customString4):\n\n" "{person_list}\n\n" "Their Google accounts [b]already exist[/b], so Helix will not " "change them automatically — a rename after creation has to touch " "Google Directory, Feishu, the SF write-back and Helix's own " "identity rows, and needs a human. Anyone without an account yet " "was corrected automatically by this job and is not listed.\n" "Usual cause: HR filled the email name in after Helix had already " "created the person and derived a given.family address.\n" "How to fix: update primaryEmail in Google Directory (the old " "address is kept as an alias automatically — do NOT delete and " "recreate), then sync Feishu / SF / Helix.\n\n" "— Helix IT Platform" ), "body_text_ja": ( "以下の [b]{count}[/b] 名の会社メールアドレスが、HR が " "SuccessFactors で指定したメール名(PerPersonal.customString4)と" "一致していません:\n\n" "{person_list}\n\n" "対象者の Google アカウントは[b]すでに作成済み[/b]のため、Helix は" "自動的に変更しません。作成後の変更は Google Directory・Feishu・" "SF への書き戻し・Helix 側の identity 行すべてに影響するため、" "担当者による対応が必要です。アカウント未作成の分は本ジョブが自動で" "修正済みで、この一覧には含まれません。\n" "よくある原因: Helix が人員レコードを作成し given.family 形式の" "アドレスを導出した後で、HR がメール名を入力したケースです。\n" "対応方法: Google Directory で primaryEmail を変更してください" "(旧アドレスは自動的にエイリアスとして保持されます。削除して" "作り直さないでください)。その後 Feishu / SF / Helix を同期します。\n\n" "— Helix IT プラットフォーム" ), }, } __all__ = ["TEMPLATES"]