跳到主要内容

CRM 服务 / 产品计费说明

::: note ::: title 注意 :::

有关产品定义、供应、附加组件和去供应的完整端到端指南,以及详细的 Ansible 示例和定价策略,请参见 完整产品生命周期指南。 :::

产品与服务概述

产品(菜单项):

产品就像餐厅菜单上的特定菜肴,例如“意大利培根意面”。

它有明确的描述、成分列表(如意大利面、奶油、鸡蛋、奶酪和培根)和价格。

在 OmniCRM 中,产品同样包含了所包含内容的详细信息——功能、规格和定价。

通常,客户可能希望进行修改,比如“去掉洋葱”或“加点额外的奶酪”。在 OmniCRM 中,这对应于在创建之前自定义服务。对服务的自定义或修改的程度由您(操作员)定义。

在 OmniCRM 中,客户或员工可能会修改产品,以更好地满足特定客户的需求,例如升级他们的互联网速度或添加特定功能。这种自定义反映在提供的特定服务中。

产品本质上是客户可以选择订购的产品,��似于从菜单上阅读和选择菜肴。

OmniCRM 产品定义

产品目录(餐厅菜单):

产品目录就像餐厅的整个菜单,列出了所有可用的菜肴——从开胃菜到甜点。

它是餐厅(或在您的情况下,服务提供商)提供的所有内容的完整集合。

在商业环境中,产品目录为客户提供所有可用产品,以便他们可以选择最符合其需求的产品。

产品管理界面

产品目录编辑视图

服务(准备好的菜肴):

当客户从菜单上点菜时,菜肴在厨房中准备。这类似于从产品创建服务。

在 OmniCRM 中,当客户选择产品时,会创建该产品的一个实例并作为服务交付。

它是专门为该客户定制和准备的,就像为就餐者准备的餐点。

例如,当某人从产品目录中选择“互联网铜牌计划”时,供应系统会“烹饪”出该计划的一个实例,所需的成分(IP 地址、调制解调器和端口)——即激活该计划并将其交付给特定客户。

捆绑产品(组合餐):

产品目录还可能提供捆绑,例如组合餐,其中包括开胃菜、主菜和甜点,以特价一起提供。

在 OmniCRM 中,捆���产品将多个单独的产品组合成一个方便的包——例如“家庭必需品捆绑”,包括互联网、有线电视和电话服务,享受折扣价格。

一旦选择,这个捆绑就会转化为多个为客户量身定制的服务。

产品定义

产品是用于创建服务/附加组件/折扣/附加功能等的模板。

在定义中,我们包括:

  • 关于产品的信息(功能、包含内容、条款和条件、合同期限、图标等),这些信息会显示给 CRM 的用户(客户或 员工)。

  • 关于谁可以购买该产品的业务逻辑(商业住宅),它是否依赖于已供应的父服务(例如,仅对拥有移动服务的客户可用的移动附加组件),它是否可以通过自助服务直接由客户订购,或者仅由客户服务代理订购,以及何时可以购买该产品(允许产品仅在设定的时间段内可用)。

  • 当要包含库存项目(例如调制解调器或 SIM 卡)时,这些项目会被指定为库存项目列表,例如以下服务需要分配一个 SIM 卡和一个电话号码:

    ['SIM Card', 'Phone Number'] 这些与 CRM 中定义的 库存项目 相关联。

  • 引用 Ansible Playbook 以供应服务 供应 Play 以及要传递��� Ansible 的变量。这些要传递的变量是魔法,因为它们可能是由我们添加的产品定义的变量,如 service_id,或者它们可能是像 ICCIDMSISDN 这样的变量,我们在分配库存时选择了库存项目。捆绑在供应 Play 中处理,以包含多个服务,例如捆绑的家庭互联网、电视和语音产品,可能为每个服务提供一个服务。

OmniCRM 产品定义

产品类别和服务类型

产品使用两个分类字段来帮助组织和过滤产品:

产品类别

category 字段控制产品在 UI 中的显示位置。 常见值包括:

  • standalone - 在创建新服务时显示为基础服务选项
  • addon - 在添加到现有服务时显示
  • bundle - 显示为捆绑服务选项(像附加组件一样供应给现有服务)
  • promo - 特别促销产品

这些类别纯粹是组织性的,不决定什么被供应。实际的供应行为完全由 provisioning_play 中引用的 Ansible playbook 决定。

例如:- standalone 产品通常创建一个新的服务对象 - addonbundle 产品通常添加到现有服务 - 但这取决于编写 playbook 的实施者 - 您可以从附加组件创建多个服务对象,或者根据需要从独立产品修改现有服务

类���仅控制 UI 流程以及客户/员工看到产品选项的位置。

服务类型

service_type 字段对提供的服务类型进行分类。

这些完全由用户定义,但常见值包括:

  • mobile - 具有语音、短信和数据的移动电话服务
  • fixed - 固定无线或有线互联网服务
  • fixed-voice - 固定电话语音服务(VoIP、座机)
  • hotspot - 移动热点或租赁设备
  • dongle - USB 调制解调器或加密狗服务
  • voice - 仅语音服务
  • data - 仅数据服务

与类别一样,服务类型可以根据您的产品进行自定义。它们有助于:

  • 过滤哪些附加组件适用于哪些基础服务
  • 在客户门户中组织产品
  • 匹配库存要求
  • 确定供应工作流

示例:拥有 mobile 服务的客户可以看到移动附加组件,而拥有 fixed 服务的客户可以看到固定线路附加组件。

管理产品

产品通过产品管理页面进行管理,您可以在此查看、搜索、过滤和编辑所有可用产品。

产品列表页面{.align-center width="800px"}

产品模态界面

单击任何产品会打开一个增强的选项卡界面,将所有产品设置组织成逻辑组,以便更轻松地导航和编辑。

���品模态 - 基本信息选项卡{.align-center width="800px"}

产品管理模态具有五个组织良好的选项卡:

  1. 📋 基本信息 - 核心产品信息(名称、别名、类别、图标、功能、条款)
  2. 💰 定价 - 所有与成本相关的字段,包括经常性成本、设置成本和税率
  3. ⚙️ 配置 - 续订设置、客户类型和依赖关系
  4. 🔧 供应 - Ansible playbook 配置和库存要求
  5. 📅 可用性 - 日期范围和系统时间戳

产品模态 - 定价选项卡{.align-center width="800px"}

定价选项卡组织:

定价选项卡将成本字段分组为逻辑部分:

  • 经常性成本 - 每月零售和批发成本并排显示
  • 设置成本 - 对零售和批发收取的一次性激活费用
  • - 税率配置及自动计算

编辑模式功能:

  • 图标选择器 - 以视觉方式搜索和选择 FontAwesome 图标
  • 库存项目选择器 - 从可用库存项目类型中选择
  • 日期/时间选择器 - 轻松选择可用窗口
  • 货币格式 - 成本字段自动添加 $ 前缀
  • 下拉选择器 - 预定义的类别和布尔字段选项

产品模态 - 基本信息编辑模式{.align-center width="800px"}

图标选择器:

在编辑图标字段时,会出现一个可搜索的图标选择器界面,允许您以视觉方式浏览和选择数千个 FontAwesome 图标。

产品模态 - 图标选择器{.align-center width="800px"}

功能: * 按关键字搜索图标(例如,“扳手”、“移动”、“wifi”) * 实时预览图标外观 * 显示图标类名称以供参考 * 下拉选择以快速访问

配置选项卡:

配置选项卡将产品行为设置组织成逻辑组。

产品模态 - 配置选项卡{.align-center width="800px"}

配置部分:

  • 续订设置:
    • 自动续订 - 默认续订行为(提示/是/否)
    • 允许自动续订 - 客户是否可以启用自动续订
    • 合同天数 - 最小合同期限(例如,30 代表每月,365 代表每年)
  • 客户类型:
    • 住宅 - 可供消费者客户使用
    • 商业 - 可供商业客户使用
  • 依赖关系:
    • 依赖于列表 - 在添加此产品之前所需的产品 ID 或服务类型
    • 用于附加组件依赖关系(例如,移动附加组件需要���动的移动服务)

供应选项卡:

供应选项卡处理 Ansible 自动化和库存要求。

产品模态 - 供应选项卡{.align-center width="800px"}

供应字段:

  • 供应 Play:
    • Ansible playbook 的名称(不带 .yaml 扩展名)
    • 必须存在于 OmniCRM-API/Provisioners/plays/ 目录中
    • 在创建、更新或去供应服务时调用
  • 供应 JSON 变量:
    • 作为 JSON 传递给 Ansible playbook 的默认变量
    • 可以在供应时覆盖
    • Playbook 接收这些变量以及 customer_id、product_id、service_id、access_token
  • 库存项目列表:
    • 多选选择器,显示可用的库存项目类型
    • 示例:SIM 卡、电话号码、调制解调器路由器、IPv4 地址
    • 客户/员工在订购时从可用库存中选择特定项目
    • 选定的库存 ID 作为变量名称传递给 playbook

可用性选项卡:

可用性选项卡控制何时可以购买该产品,并显示系统元数据。

产品模态 - 可用性选项卡{.align-center width="800px"}

可用性设置:

  • 可用开始:
    • 产品开始可供购买的日期/时间
    • 留空以实���即时可用性
    • 对于预先宣布的新产品很有用
  • 可用结束:
    • 产品不再可供购买的日期/时间
    • 留空以实现无限期可用性
    • 非常适合限时促销或停产产品
  • 系统元数据(只读):
    • 创建 - 产品首次创建的时间戳
    • 最后修改 - 最近更新的时间戳
    • 由系统自动维护

模态操作:

  • 查看模式:
    • 关闭 - 关闭模态
    • 克隆产品 - 创建一个带有“_clone”后缀的副本
    • 编辑产品 - 切换到编辑模式
  • 编辑/创建模式:
    • 取消 - 丢弃更改并关闭
    • 保存更改 - 创建或更新产品(大按钮以强调)

产品字段

产品模型包含定义产品及其应如何供应所需的所有信息。这些字段通过上述产品管理模态界面进行管理。

基本信息

  • product_id - 系统自动分配的唯一标识符
  • product_name - 显示给客户和员工的名称
  • product_slug - 用于 URL 和 API 调用的唯一标识符(小写,无空格,使用连字符)
  • category - 控制该产品在 UI 中的显示位置:
    • standalone - 在创建新服务时显示为基础服务选项
    • addon - 在添加到现有服务时显示
    • bundle - 显示���捆绑服务选项
    • promo - 特别促销产品
  • service_type - 提供的服务类型(例如,移动、固定、固定语音、热点、加密狗、语音、数据)。用于过滤哪些附加组件适用于哪些服务。
  • comment - 关于产品的内部备注,仅供员工参考(不向客户显示)
  • icon - 在 UI 中显示的 FontAwesome 图标类(例如,fa-solid fa-sim-card

定价字段

  • retail_cost - 向客户收取的每月经常性费用(对于一次性购买或预付产品设置为 0)
  • wholesale_cost - 提供此服务的每月成本(用于利润计算)
  • retail_setup_cost - 向客户收取的一次性激活或设置费用
  • wholesale_setup_cost - 设置服务的您的一次性费用
  • tax_percentage - 应用于该产品的税率(例如,10 代表 10%,12.5 代表 12.5%)。对于免税产品设置为 0。此税率会自动应用于从该产品创建的交易。

产品税配置{.align-center width="800px"}

税务应用:

当从该产品创建交易时,税率会自动复制到交易中,税额会被计算。例如:

  • 税率为 10% 的产品,零售成本为 $50.00 → 交易有 $5.00 税
  • 税率为 0% 的产品(免税)→ 交易有 $0.00 ��
  • 手动交易覆盖 → 员工可以按交易更改税率

客户可见性和访问

  • enabled - 此产品是否处于活动状态并可供购买(设置为 false 以隐藏而不删除)
  • residential - 住宅(消费者)客户是否可以购买此产品
  • business - 商业(商业)客户是否可以购买此产品
  • customer_can_purchase - 客户是否可以通过门户自助购买(true)或仅员工可以添加(false)
  • available_from - 此产品开始可供购买的日期/时间(可选)
  • available_until - 此产品不再可供购买的日期/时间(可选,适用于限时优惠)

合同和续订

  • contract_days - 最小合同期限(以天为单位)(例如,30 代表每月,365 代表每年,0 代表没有最小合同)
  • auto_renew - 默认续订行为:
    • prompt - 每次询问客户是否续订
    • true - 自动续订,无需询问
    • false - 需要手动续订
  • allow_auto_renew - 客户是否可以启用自动续订(对于一次性购买设置为 false)

面向客户的内容

  • terms - 在购买前向客户显示的条款和条件(包括限制、到期规则、使用条件)
  • features_list - 向客户显示的功能和包含内容列表(Python 列表格式:['Feature 1', 'Feature 2']

供应配置

  • provisioning_play - 供应此服务的 Ansible playbook 名称(不带 .yaml 扩展名)。必须存在于 OmniCRM-API/Provisioners/plays/ 目录中。
  • provisioning_json_vars - 作为 JSON 传递给 Ansible playbook 的默认变量。这些可以在供应时被覆盖。Playbook 接收这些变量以及 customer_idproduct_idservice_idaccess_token
  • inventory_items_list - 此产品所需的库存项目列表(例如,['SIM Card', 'Mobile Number'])。当客户下订单时,他们将被提示从可用库存中选择特定项目。选定的库存 ID 作为变量名称传递给供应 playbook。
  • relies_on_list - 在添加此产品之前必须存在的产品 ID 或服务类型列表。用于附加组件依赖关系(例如,移动附加组件需要活动的移动服务)。

系统元数据

  • created - 产品创建的时间戳(自动设置)
  • last_modified - 产品最后更新的时间戳(自动更新)

示例产品定义

独立产品(移动 SIM)

OmniCRM 产品定义

{
"product_id": 1,
"product_slug": "Mobile-SIM",
"product_name": "Mobile SIM Only",
"category": "standalone",
"service_type": "mobile",
"provisioning_play": "play_psim_only",
"provisioning_json_vars": "{\"iccid\": \"\", \"msisdn\": \"\"}",
"inventory_items_list": "['SIM Card', 'Mobile Number']",
"retail_cost": 0,
"retail_setup_cost": 0,
"wholesale_cost": 3,
"wholesale_setup_cost": 1,
"contract_days": 0,
"residential": true,
"business": true,
"enabled": true,
"customer_can_purchase": true,
"icon": "fa-solid fa-sim-card",
"features_list": "['Australian Phone Number (04xxx)', 'Fastest speeds', 'Best coverage', 'Roaming on the Mainland']",
"terms": "Must be activated within 6 months. All credit lost if service is not used for 12 months.",
"comment": "Physical SIM card for use with Mobile Phones"
}

此独立产品需要两个库存项目(SIM 卡和移动号码),并在供应时创建一个新服务。

附加产品(每月数据计划)

{
"product_slug": "norfone-mobile-prepaid-mini",
"product_name": "Norfone Mini Plan",
"category": "addon",
"service_type": "mobile",
"provisioning_play": "play_topup_charge_then_action",
"provisioning_json_vars": "",
"inventory_items_list": "[]",
"retail_cost": 30,
"retail_setup_cost": 0,
"wholesale_cost": 5.84,
"contract_days": 30,
"residential": true,
"business": false,
"enabled": true,
"customer_can_purchase": true,
"auto_renew": "prompt",
"icon": "fa-solid fa-sim-card",
"features_list": "['8GB of Ultra fast data', 'Unlimited Calls & Texts to Norfone users', '100 Minutes of Talk to Australia', '100 SMS to Australia', '30 Day Expiry']",
"terms": "Credit expires after 30 days. Once data, voice or sms is used up, you will need to top up to continue using the service.",
"comment": "Our smallest plan for light users"
}

此附加产品不需要库存,并应用于现有服务。它向客户收费并向其服务添加信用/余额。

捆绑产品(老年人套餐)

{
"product_slug": "Bundle-Seniors",
"product_name": "Seniors Bundle",
"category": "bundle",
"service_type": "fixed",
"provisioning_play": "play_seniors_package",
"provisioning_json_vars": "{\"IPTV_Service_ID\": \"SeniorBundle\"}",
"inventory_items_list": "['Modem Router']",
"retail_cost": 30,
"retail_setup_cost": 0,
"wholesale_cost": 10,
"wholesale_setup_cost": 11,
"contract_days": 180,
"residential": true,
"business": false,
"enabled": true,
"icon": "fa-solid fa-person-walking-with-cane",
"features_list": "['20Mbps Download', '5Mbps Upload', 'Unlimited Data', 'Home Voice', 'TV: Extra +£5 per month', '£60 Installation Fee']",
"terms": "6 Month Contract, must show senior citizen's card to qualify",
"comment": "20Mbps/2Mbps GPON Service + IPTV + Phone"
}

此捆绑产品通过单个 playbook 供应多个服务(互联网 + IPTV + 电话)。它需要一个库存项目(调制解调器路由器)。

附加产品(简单充值)

{
"product_slug": "Mobile-Topup-5",
"product_name": "PAYG £5 Topup",
"category": "addon",
"service_type": "mobile",
"provisioning_play": "play_topup_monetary",
"provisioning_json_vars": "{\"service_id\": \"\"}",
"inventory_items_list": "[]",
"retail_cost": 5,
"retail_setup_cost": 0,
"wholesale_cost": 0,
"contract_days": 0,
"residential": true,
"business": false,
"enabled": true,
"customer_can_purchase": true,
"icon": "fa-solid fa-coins",
"features_list": "['£5 credit', 'Valid for 180 days']",
"terms": "Valid for 180 days or until all credit is used. See our website for full rates",
"comment": "£5 to use for Calls, SMS & Data"
}

此附加产品仅向现有服务添加货币信用。无需库存,并使用 service_id 来识别要充值的服务。

变量如何传递给 Ansible

了解变量如何从产品定义通过 API 流向 Ansible playbook 对于编写有效的供应 playbooks 至关重要。

变量来源和合并

当创建供应作业时,变量来自多个来源,并按以下顺序合并(后来的来源覆盖较早的来源):

  1. 产品的 provisioning_json_vars - 来自产品定义的默认变量
  2. 请求体 - 在 API 调用中传递的变量(可以覆盖产品默认值)
  3. 系统添加的变量 - 由供应系统自动添加
  4. 库存选择 - 选定库存项目的 ID(如果 inventory_items_list 不为空)

变量合并过程

系统从所有来源合并变量,后来的来源覆盖较早的来源。这允许在供应时灵活自定义。

例如,如果您的产品具有:

"provisioning_json_vars": "{\"monthly_cost\": 50, \"data_gb\": 100}"

而您的 API 请求包括:

{
"product_id": 10,
"customer_id": 456,
"monthly_cost": 45,
"custom_param": "value"
}

最终传递给 Ansible 的 extra_vars 将是:

{
"monthly_cost": 45, # 从请求中覆盖
"data_gb": 100, # 来自 provisioning_json_vars
"product_id": 10, # 来自请求
"customer_id": 456, # 来自请求
"custom_param": "value", # 来自请求
"access_token": "eyJ..." # 系统添加
}

系统添加的变量

供应系统自动添加:

  • access_token - 用于验证 API 调用回 CRM 的 JWT 令牌(来自 g.access_token 用于 IP/API 密钥身份验证,或从 refresh_token 生成的用户身份验证)
  • initiating_user - 触发供应的用户 ID(或自动化系统的第一个管理员)
  • 请求体中的任何字段(product_idcustomer_idservice_id 等)

库存变量

当产品需要库存项目时(例如,inventory_items_list: "['SIM Card', 'Mobile Number']"),过程如下:

  1. UI/API 提示选择 - 用户从可用库存中选择特定库存项目
  2. 库存 ID 被添加到变量中 - 选定的库存项目 ID 被添加,库存类型作为变量名称
  3. Playbook 访问库存 ID - 供应 playbook 然后可以从 CRM API 检索完整的库存详细信息

例如,如果用户选择:- SIM 卡,库存 ID:789 - 手机号码,库存 ID:101

传递给 playbook 的变量包括:- SIM Card: 789 - Mobile Number: 101

Playbook 然后可以使用这些 ID 从 CRM API 获取完整的库存记录(ICCID、IMSI、MSISDN 等),并使用该信息在网络设备上供应服务。

Ansible 如何接收变量

供应系统将所有合并的变量作�� extravars 传递给 Ansible playbook。在 playbook 内部,这些变量通过 Ansible 的标准变量系统可用,并可以在任务中使用。

变量可以在 playbook 任务中直接使用 {{ variable_name }} 语法引用。例如,{{ product_id }}{{ customer_id }}{{ monthly_cost }} 等。

传递给附加产品的变量

当供应附加产品时,系统会自动传递:

  • product_id - 正在供应的附加产品的 ID
  • customer_id - 拥有该服务的客户
  • service_id - 此附加组件要添加到的服务的 ID(对附加组件至关重要)
  • access_token - 用于 API 调用的身份验证令牌
  • 来自 provisioning_json_vars 的任何变量
  • 来自 API 请求的任何附加变量

示例附加产品供应流程

当客户将“£5 充值”附加组件添加到他们的移动服务(service_id: 123)时,playbook 接收的变量包括:

  • product_id: 45(充值产品)
  • customer_id: 456(客户)
  • service_id: 123(要添加信用的服务)
  • access_token: 身份验证令牌
  • 以及来自产品的 provisioning_json_vars 的任何变量

然后,playbook 使用这些变量来:

  1. 从 CRM API 获取服务详细信息,使用 service_id
  2. 从服务记录中提取服务 UUID 和其他信息
  3. 向��费系统(OCS)添加信用,使用服务 UUID
  4. 在 CRM 中记录交易 以便计费

此流程允许附加组件准确识别要修改的服务并适当地应用更改。

独立产品与附加产品变量的区别

独立产品接收:

  • product_id - 正在供应的产品
  • customer_id - 订购服务的客户
  • 如果产品需要库存项目,则包括库存项目 ID(例如,SIM CardMobile Number
  • access_token - 用于 API 身份验证

附加产品接收:

  • product_id - 正在供应的附加产品
  • customer_id - 拥有该服务的客户
  • service_id - 要修改的现有服务的 ID(这是关键区别)
  • access_token - 用于 API 身份验证

关键区别是 service_id - 这告诉 playbook 要修改或添加到哪个现有服务。

捆绑产品

捆绑产品的供应方式类似于附加产品,但它们的 playbook 可能会创建多个服务记录。它们接收与附加产品相同的变量,包括:

  • product_id - 捆绑产品
  • customer_id - 客户
  • service_id - 父服务(如果适用)
  • 如果需要,库存项目 ID(例如,Modem Router
  • access_token - 用于 API 身份验证

捆绑 playbook(例如,play_seniors_package)然后创建多个相关服务(互联网、IPTV、���话)并将它们链接在一起。

服务

服务是属于客户的产品实例,客户为其计费。

它本质上是指向 OCS(在线计费系统)账户的链接,该账户处理费用生成和账户的实际余额和使用情况。OCS 由 CGRateS 提供支持,管理货币余额、单位余额(数据、语音、短信)、自动续订的 ActionPlans 和支出限制的 ThresholdS。

添加服务:产品选择和过滤

在向客户添加服务(无论是新的独立服务还是对现有服务的附加组件)时,系统在轮播界面中显示可用产品。显示的产品根据多个标准进行过滤:

独立服务的产品过滤

在为客户创建新服务时,UI 根据以下条件过滤产品:

  1. 客户类型 - 产品被分类为:
    • 个人(住宅)residential = truebusiness = false 的产品
    • 商业business = true 的产品
  2. 类别 - 产品分为:
    • 服务计划category = standalonebundle 的产品
    • 附加组件category = addon 的产品(在单独的轮播中显示)
  3. 可用性 - 仅在以下情况下显示产品:
    • enabled = true - 产品处于活动状态且未禁用
    • 当前日期在 available_fromavailable_until 之间 - 产品���其可用性窗口内
    • customer_can_purchase = true(如果客户是自助购买) - 产品允许直接客户购买

::: note ::: title 注意 :::

API 级过滤:API 自动按启用状态和可用日期在两个级别过滤产品:

  • 购买/选择端点 (/crm/product/) - 由附加组件模态和计划列表用于产品选择。自动过滤以仅显示在其可用日期范围内启用的产品。这确保客户和员工只能选择当前可供购买的产品。
  • 管理端点 (/crm/product/paginated) - 由产品管理页面使用。显示所有产品,包括禁用和超出可用日期的产品,允许管理员管理完整的产品目录,包括非活动产品。

include_disabled=true 传递给基础产品端点以绕过过滤(仅供管理使用)。 :::

UI 为以下内容显示单独的轮播:

  • 个人服务计划 - 住宅客户的产品
  • 商业服务计划 - 商业客户的产品
  • 个人附加组件 - 住宅附加包
  • 商业附加组件 - 商业附加包

附加服务的产品过滤

在向 现有服务 添加附加组件时,应用额外的过滤:

  1. 服务类型匹配 - 仅显示匹配的 service_type 的附加组件:
    • 如果现有服务的 service_type = "mobile",则仅显示 service_type = "mobile" 的附加组件
    • 这确保移动客户仅看到移动附加组件,互联网客户仅看到互联网附加组件,等等。
  2. 依赖关系检查 - 如果附加组件具有 relies_on_list
    • 系统检查客户是否拥有所需的产品/服务
    • 仅显示满足其依赖关系的附加组件
  3. 相同客户类型过滤 - 附加组件仍按 residentialbusiness 过滤,以匹配客户类型

示例过滤场景

对于拥有现有移动服务的商业客户(service_type = "mobile"):

  • 显示的独立产品:所有商业独立/捆绑产品(business = truecategory != "addon"
  • 显示的附加产品:仅商业移动附加组件(business = truecategory = "addon"service_type = "mobile"
  • 隐藏的产品:住宅产品、其他服务类型的附加组件(互联网、语音等)、禁用产品

服务字段

服务模型包含跟踪供应服务实例及其与客户、产品和计费系统关系的字段。

基本服务信息

  • service_id - 系统自动分配的唯一标识符(只读)
  • customer_id - 链接到拥有此服务的客户(创建后只读)
  • product_id - 链接到创建此服务的产品(创建后只读)
  • service_name - 显示给客户的名称(可编辑)
  • service_type - 服务类型:移动、互联网、VoIP、IPTV、捆绑等(可编辑)
  • service_uuid - 用于 OCS/CGRateS 计费的唯一标识符(只读,自动生成)
  • icon - 自助服务门户中显示的 FontAwesome 图标类(可编辑)

服务状态和日期

  • service_status - 当前状态:活动、非活动、暂停等(可编辑)
  • service_provisioned_date - 服务首次供应的日期(自动设置,只读)
  • service_active_date - 服务变为活动的日期(可编辑)
  • service_deactivate_date - 服务到期或将被停用的日期(可编辑)
  • contract_end_date - 合同承诺的结束日期(可编辑)

计费和定价

  • retail_cost - 向客户收取的每月经常性费用(可编辑)
  • wholesale_cost - 提供服务的成本(可编辑)
  • service_billed - 此服务是否出现在发票上(可编辑,默认:true)
  • service_taxable - 此服务是否适用税费(可编辑,默认:true)
  • invoiced - 服务是否已开票(由计费系统自动设置)
  • promo_code - 创建服务时使用的促销代码(可编辑)

客户可见性

  • service_visible_to_customer - 客户是否可以在自助服务门户中看到此服务(可编辑,默认:true)
  • service_usage_visible_to_customer - 客户是否可以查看使用/余额详细信息(可编辑,默认:true)

供应配置

  • provisioning_play - 用于供应此服务的 Ansible playbook(从产品继承,只读)
  • provisioning_json_vars - 传递给供应 playbook 的变量(从产品继承,只读)
  • deprovisioning_play - 在去供应服务时运行的 Ansible playbook(只读)
  • deprovisioning_json_vars - 去供应 playbook 的变量(只读)

服务关系

  • bundled_parent - 如果此服务是捆绑的一部分,则为父服务的 service_id(只读)
  • site_id - 链接到提供服务的物理站点/位置(可编辑)

备注和元数据

  • service_notes - 关于服务的内部备注,仅供员工参考(可编辑)
  • created - 服务创建的时间戳(自动设置,只读)
  • last_modified - 最后更新的时间戳(自动更新,只读)

可编辑字段与只读字段

通过 API/UI 可编辑:

服务可以通过 PATCH /crm/service/{service_id} 更新以下字段:

  • service_name、service_type、service_status
  • service_notes
  • retail_cost、wholesale_cost
  • service_billed、service_taxable
  • service_visible_to_customer、service_usage_visible_to_customer
  • service_active_date、service_deactivate_date、contract_end_date
  • icon、promo_code、site_id

只读(自动设置):

这些字段在创建后无法直接修改:

  • service_id、customer_id、product_id
  • service_uuid(在供应期间生成)
  • service_provisioned_date
  • provisioning_play、provisioning_json_vars
  • deprovisioning_play、deprovisioning_json_vars
  • bundled_parent
  • invoiced(由计费系统管理)
  • created、last_modified(自动管理)

将产品供应到服务

供应过程通过一系列协调步骤将产品(模板)转换为服务(特定于客户的实例),涉及 Web UI、API 和 Ansible playbooks。

高级供应流程

  1. 预供应设置 - 在 API 中创建产品并配置供应,并编写和测试相应的 Ansible playbooks
  2. 服务选择 - 从客户页面,员工或客户选择“添加服务”
  3. 产品过滤 - 显示的产品根据以下条件过滤:
    • 客户类型(住宅/商业)
    • 现有服务(用于附加组件依赖关系中的 relies_on_list
    • 可用日期(available_from/available_until
    • enabledcustomer_can_purchase 标志
  4. 自定义 - 选项覆盖供应变量(用于价格调整、自定义配置等)
  5. 库存选择 - 如果产品需要库存(inventory_items_list 不为空),用户选择特定项目(例如,选择哪个 SIM 卡、哪个电话号码)
  6. 供应启动 - 当单击“供应”按钮时,API 创建一个供应作业

详细的 API 和 Ansible 集成流程

当供应服务时,发生以下顺序:

步骤 1:创建供应作业 (/routes/service.py)

API 接收供应请求并调用 create_provisioning_job()services/provisioning_service.py,参数包括:

  • provisioning_play - Ansible playbook 的名称(例如,play_psim_only
  • provisioning_json_vars - 来自产品或请求覆盖的变量的 JSON 字符串
  • customer_id - 订购服务的客户 ID
  • product_id - 正在供应的产品 ID
  • service_id - (可选)现有服务的 ID,用于附加组件
  • 库存选择 - 选定库存项目的 ID

步骤 2:变量组装 (services/provisioning_service.py)

供应服务从多个来源按以下顺序合并变量:

  1. 产品的 provisioning_json_vars(来自产品定义的默认值)
  2. 请求体参数(可以覆盖产品默认值)
  3. 系统添加的变量:
    • access_token - 用于 API 身份验证回 CRM 的 JWT 令牌
    • initiating_user - 触发供应的用户 ID
    • customer_idproduct_idservice_id
  4. 库存选择 - 作为 {inventory_type: inventory_id} 对添加

示例合并变量:

{
"customer_id": 123,
"product_id": 456,
"service_id": 789, # 仅用于附加组件
"SIM Card": 1001, # 来自库存选择
"Mobile Number": 1002, # 来自库存选择
"monthly_cost": 30, # 来自 provisioning_json_vars
"data_gb": 50, # 来自 provisioning_json_vars
"access_token": "eyJ...", # 系统添加的用于 API 回调
"initiating_user": 5 # 系统添加的
}

步骤 3:创建供应记录 (models.py - Provision 模型)

在数据库中创建一个 Provision 记录,包含:

  • provision_id - 用于跟踪的唯一标识符
  • provisioning_play - playbook 文件名
  • provisioning_json_vars - 合并的变量作为 JSON 字符串
  • task_count - playbook 中的任务数量(从 YAML 中提取)
  • provisioning_status - 状态代码(最初设置为 1 = 运行,然后更新为 0 = 成功、2 = 失败,或者如果仍在进行中可能保持 1)
  • product_idcustomer_idservice_id - 上下文引用

步骤 4:后台 Playbook 执行 (Provisioners/playbook_runner_v2.py)

API 启��一个后台线程:

  1. OmniCRM-API/Provisioners/plays/{playbook_name}.yaml 加载 playbook YAML
  2. 调用 ansible_runner.run(),参数包括:
    • playbook - 加载的 YAML 文件的路径
    • extravars - 所有合并的变量(传递给 Ansible)
    • inventory - 设置为 'localhost,'(本地执行)
    • event_handler - 自定义处理程序以捕获任务执行事件
  3. 实时监控 playbook 执行

步骤 5:事件捕获和日志记录 (ProvisioningEventHandler)

随着每个 Ansible 任务的执行,事件被捕获并存储为 Provision_Event 记录:

  • event_name - 来自 playbook 的任务名称
  • event_number - 序列号
  • provisioning_status - 指示任务结果的状态代码:
    • 0 = 成功 - 任务成功完成
    • 1 = 运行 - 任务正在执行
    • 2 = 失败 - 关键失败,停止供应
    • 3 = 失败(忽略) - 任务失败,但错误被忽略(ignore_errors: true 在 playbook 中)
  • provisioning_result_json - 任务结果,敏感数据被编辑

事件处理程序会自动从日志中剥离密码、密钥、秘密和其他敏感数据。

步骤 6:Ansible Playbook 执行 (Provisioners/plays/*.yaml)

Ansible playbook 在本地运行,通常执行以下操作:

a. 获取产���定义 - 使用 {{ access_token }}/crm/product/product_id/{{ product_id }} 发送 GET 请求

b. 获取客户信息 - 向 /crm/customer/customer_id/{{ customer_id }} 发送 GET 请求

c. 处理库存项目(如果需要) - 对每个选定项目向 /crm/inventory/inventory_id/{{ inventory_id }} 发送 GET 请求以检索完整详细信息(ICCID、MSISDN、序列号等)

d. 配置外部系统 - 进行 API 调用:

  • HSS(家庭用户服务器)进行用户供应
  • IMS(IP 多媒体子系统)进行语音注册
  • CGRateS/OCS 创建帐户、计费配置、费率计划
  • ENUM 服务器进行电话号码映射
  • 网络设备(路由器、交换机等)

e. 添加设置费用(如果适用) - 向 /crm/transaction/ 发送 POST 请求以记录一次性费用

f. 向客户收费 - 向 OCS/CGRateS 发送 POST 请求以收取配置的 retail_setup_cost

g. 创建 OCS 账户 - 向 OCS/CGRateS 发送 POST 请求以使用 UUID 创建计费账户

h. 配置经常性费用 - 在 OCS/CGRateS 中为每月经常性费用创建 Actions 和 ActionPlans

i. 创建服务记录 - 向 /crm/service/ 发送 PUT/POST 请求以在 CRM 中创建服务记录:

{
"customer_id": 123,
"product_id": 456,
"service_name": "Mobile SIM - 0412345678",
"service_uuid": "generated-uuid-for-ocs",
"service_status": "Active",
"service_type": "mobile",
"retail_cost": 30,
"wholesale_cost": 5,
"provisioning_play": "play_psim_only",
"provisioning_json_vars": "{...}"
}

j. 分配库存 - 向 /crm/inventory/inventory_id/{{ inventory_id }} 发送 PATCH 请求,以将库存标记为“分配”给服务

k. 发送通知(可选) - 向客户发送电子邮件或短信,提供服务详细信息

步骤 7:完成和状态更新

当 playbook 完成时:

  • 成功Provision.provisioning_status 更新为 0(成功)
  • 关键失败Provision.provisioning_status 更新为 2(失败),并向 crm_config.provisioning.failure_list 发送失败电子邮件
  • 非关键失败:任务失败且 ignore_errors: true 的任务标记为状态 3(失败但被忽略),不会停止供应

已供应的服务现在在 CRM 中可见,并对客户处于活动状态(如果供应成功)。

关键区别:独立产品、附加产品与捆绑产品的供应

独立产品category: standalone):

  • 接收 customer_idproduct_id
  • 通常需要库存项目(SIM 卡、电话号码、���制解调器)
  • 通过 API PUT /crm/service/ 创建 服务记录
  • 在外部系统上供应新资源(HSS、OCS、网络设备)
  • 示例:新移动 SIM 激活、新互联网连接

附加产品category: addon):

  • 接收 customer_idproduct_idservice_id(要修改的现有服务)
  • 通常不需要库存(或仅需少量库存)
  • 修改现有 服务或向现有 OCS 账户添加费用
  • 可能在 OCS 上执行操作(添加数据包、添加信用、启用功能)
  • 不创建新服务记录(或创建与父服务关联的子服务记录)
  • 示例:每月数据计划充值、国际漫游包、额外信用

捆绑产品category: bundle):

  • 在接收的变量方面与附加产品类似
  • 可能需要一些库存项目(例如,家庭捆绑的调制解调器)
  • 创建 多个 相关服务记录(互联网 + 电视 + 电话)
  • 在不同系统上供应多个资源
  • 在 CRM 中将服务链接在一起以实现统一计费/管理
  • 示例:家庭捆绑(互联网 + IPTV + VoIP 电话)

供应 playbook 要求

为了使 playbook 正常工作,它必须:

  1. 位于 OmniCRM-API/Provisioners/plays/{playbook_name}.yaml
  2. 通过 Ansible 的 extravars 接受变量(通过 {{ variable_name }} 访问)
  3. 使用 Authorization: Bearer {{ access_token }} 头进行 API 调用身份验证
  4. 优雅地处理失败,在适当的地方使用 rescue 块和 ignore_errors
  5. 为独立产品创建服务记录,或修改附加产品的现有服务
  6. 如果选择了库存,则分配库存
  7. 在发生关键错误时通过 fail 模块返回有意义的错误消息

Playbooks 中的常见变量

每个 playbook 都会接收这些变量:

  • customer_id - 整数,订购服务的客户
  • product_id - 整数,正在供应的产品
  • service_id - 整数(仅限附加组件/捆绑),要修改的现有服务
  • access_token - 字符串,CRM API 身份验证的 JWT 令牌
  • initiating_user - 整数,触发供应的用户
  • 以及任何库存项目 ID:{{ inventory_type }}: inventory_id
  • 以及来自 provisioning_json_vars 的任何变量
  • 以及在供应请求中传递的任何变量

Playbooks 可以使用这些变量来:

  • 获取完整的产品详细信息: GET /crm/product/product_id/{{ product_id }}
  • 获取客户详细信息: GET /crm/customer/customer_id/{{ customer_id }}
  • 获取库存详细信息: GET /crm/inventory/inventory_id/{{ SIM_Card }}
  • 创建交易:POST /crm/transaction/
  • 创建���务:PUT /crm/service/
  • 更新服务:PATCH /crm/service/{{ service_id }}
  • 分配库存: PATCH /crm/inventory/inventory_id/{{ inventory_id }}

示例:简单附加产品的 Playbook 流程

对于移动数据充值附加组件:

  1. Playbook 接收:customer_idproduct_idservice_idaccess_token
  2. 获取服务详细信息:GET /crm/service/{{ service_id }}
  3. 获取产品详细信息: GET /crm/product/product_id/{{ product_id }}
  4. 在 OCS 中向客户收费:POST to CGRateS 从余额中扣除 retail_cost
  5. 在 OCS 中添加数据信用:POST to CGRateS 添加到期的数据余额
  6. 在 CRM 中记录交易:POST /crm/transaction/ 以获取收费详细信息
  7. 成功完成

整个过程在 ProvisionProvision_Event 表中跟踪,以便于调试和审计。

OCS 参与

OCS(在线计费系统),通过 CGRateS 实现,处理所有实时计费和服务使用跟踪。CRM 服务记录充当指向 OCS 账户的指针,后者管理:

  • 经常性费用 - 每月费用、DID 租赁、订阅费用
  • 基于使用的计费 - 每分钟语音通话、每 MB 数据、每条短信费用
  • 余额管理 - 货币余额(预付信用)和单位余额(数据 GB、语音分钟、短信计数)
  • 余额转换 - 将货币���额转换为单位余额(例如,花费 $30 获取 10GB 数据包)
  • 账户状态 - 根据信用限额和阈值的活动、暂停、禁用

CRM 服务记录包含元数据和配置(客户、产品、定价、可见性),而 OCS 包含实时计费状态(余额、使用情况、费用)。

检索服务使用情况和余额

服务使用信息从 OCS/CGRateS 检索,并实时显示给客户和员工。

如何检索使用情况

当请求服务的使用情况时(通过 UI 或 API),发生以下流程:

  1. API 请求 - 前端调用 GET /crm/service/{service_id} 或在 UI 中查看服务详细信息

  2. 服务查找 - API 从数据库中检索服务记录,提取 service_uuid

  3. CGRateS API 调用 - cgrates_service.py 模块向 CGRateS 发出两个调用:

    a. Get_Balance(service_uuid) - 检索账户余额,使用 BalanceMap

    • 返回按类型组织的余额:数据、语音、短信、货币、数据加密狗
    • 每个余额包括:ID、值、到期日期、权重、目标 ID
    • 系统添加人类可读字段:custom_Name_hrcustom_Expirationcustom_Description_String b. Get_ActionPlans(service_uuid) - 检索活动的自动续订行动计划(在下一节中介绍)
  4. 响应合并 - CGRateS 数��合并到服务响应中:

    {
    "service_id": 123,
    "service_name": "Mobile Service",
    "service_uuid": "abc-123-def",
    "cgrates": {
    "BalanceMap": {
    "DATA": [{
    "ID": "DATA_10GB",
    "Value": 5368709120,
    "ExpirationDate": "2025-02-01T00:00:00Z",
    "custom_Name_hr": "10GB Data Pack",
    "custom_Expiration": "Feb 1, 2025",
    "custom_Description_String": "5 GB remaining"
    }],
    "VOICE": [{
    "ID": "VOICE_UNLIMITED",
    "Value": 999999999,
    "custom_Name_hr": "Unlimited Calls",
    "custom_Description_String": "Unlimited minutes"
    }],
    "MONETARY": [{
    "ID": "PREPAID_CREDIT",
    "Value": 25.50,
    "custom_Description_String": "$25.50 credit"
    }]
    },
    "ActionPlans": [...]
    }
    }
  5. UI 显示 - 前端组件显示使用数据:

    • ServiceUsage.js - 主使用显示组件,每 3 秒自动刷新
    • UsageCard.js - 每种余额类型的摘要卡
    • UsageProgress.js - 显示使用/剩余百分比的进度条
    • 余额以颜色编码并格式化以提高可读性

使用数据结构

BalanceMap 中的每个余额包含:

CGRateS 原生字段:

  • ID - 余额的唯一标识符(例如,“DATA_10GB_2025_01”)
  • Value - 余额金额:
    • 对于数据:字节(5368709120 = 5 GB)
    • 对于语音:秒(3600 = 1 小时)
    • 对于短信:计数(100 = 100 条消息)
    • 对于货币:货币单位(25.50 = $25.50)
  • ExpirationDate - 余额到期的 ISO 8601 时间戳
  • Weight - 余额消耗的优先级(优先消耗权重高的)
  • DestinationIDs - 此余额适用的目的地(例如,[“AU”,“INTERNATIONAL”])

人类可读字段(由 CRM 添加):

  • custom_Name_hr - 从 ID 提取的人类可读名称
  • custom_Expiration - 格式化的到期日期(例如,“2025 年 1 月 15 日”或“11 天后”)
  • custom_Description_String - 人类可读的余额描述:
    • 数据:“5 GB 剩余”或“10 GB 总计”
    • 语音:“60 分钟剩余”或“无限制”
    • 短信:“50 条短信剩余”
    • 货币:“$25.50 信用”

使用可见性控制

服务使用可见性由两个字段控制:

  • service_visible_to_customer - 如果为 false,则服务在客户的自助服务门户中完全隐藏
  • service_usage_visible_to_customer - 如果为 false,则服务可见,但使用/余额详细信息隐藏(客户可以看到他们有服务,但不知道他们使用了多少)

这允许操作员:

  • 隐藏客户的内部/测试服务
  • 显示服务存在而不透露使用情况(对无限计划或敏感服务有用)
  • 完全透明的使用显示(默认)

实时使用更新

Web UI 自动刷新使用数据:

  • 间隔:每 3 秒(在 ServiceUsage 组件中可配置)
  • 方法:轮询 GET /crm/service/{service_id},从 CGRateS 获取实时数据
  • 效率:仅活动服务视图刷新;列表视图使用缓存数据

这确保客户和员工在使用发生时看到近实时的余额更新。

经常性费用 / 自动续订

经常性费用,例如每月服务费或每 DID 收费,首先在 OCS 中作为操作创建,格式为 Action_ServiceUUID_ServiceName_WhatitDoes

例如,对于每月 $60 的 GPON 服务,包括 1TB 的使用量,操作可能如下所示:

Action_kj49-adsf-1234-9742_60_GPON_1TB_MonthlyExpiry

  1. 将货币余额重置为 $0
  2. 向 CRM 的 /simple_provision 发送 HTTP POST 请求以供应某些内容
  3. 添加 1TB 使用量的信用,过期时间为 1 个月

如果我们希望 MRC 是经常性的(我们希望),那么我们将创建一个名为 ActionPlan_{{ service_uuid }}_Monthly_ChargeActionPlan,其时间设置为 每月 以每月触发,并将 ActionPlan 分配给账户。

我们可以根据 年/月/天 参数设置到期日期,以便在此点停止 MRC,例如对于固定的 12 个月服务。

由于操作和 ActionPlans 对每项服务都是唯一的,因此它们不会与其他服务共享任何内容。

这意味着我们可以使用调整后的值进行供应,而不会影响其他服务。

附加组件和附加功能

附加组件/附加功能,例如购买额外数据、漫游包、国际分钟等,处理方式与此类似。创建一个操作以执行所需的操作,例如收取货币值,然后授予具有设定到期的单位余额。

我们不是使用 ActionPlans 自动将其添加到账户,而是从 Ansible 中一次性触发我们刚刚创建的 ExecuteAction

添加预付货币余额

对于预付货币余额,例如 $10 的 PAYG 计划,这被添加为货币余额,但优先级更高。

这些服务的默认余额的信用限额将为 $0。

信用限额 / 防止过度支出

ThresholdS 用于每个账户设置给定时间段的最大支出。

对于 PAYG / 预付客户,这为 $0。

通过 CRM 与 OCS 交互

对于每项服务,您可以在 CRM API 中查看 BalancesActionPlansActionsThresholdS

可以根据需要从 CRM API 中删除 ActionPlans,通过 Ansible Playbooks 执行。可以根据需要从 CRM 中添加 ActionPlans,通过添加附加组件/服务并通过 Ansible Playbooks 执行。

可以禁用 OCS 账户,这将停止 ActionPlans 的执行和服务的消费。

对于信用限额,按产品政策设置 ThresholdS 值。

在 CRM 中查看和管理 ActionPlans

ActionPlans(自动续订配置)通过 CRM 界面显示和管理,允许员工和客户查看即将到来的自动续订并进行管理。

如何检索和显示 ActionPlans

在 CRM 中查看服务时,ActionPlans 会自动获取并显示:

  1. API 调用 - 当调用 GET /crm/service/{service_id} 时,API:

    • 从数据库中检索服务记录
    • 提取 service_uuid(OCS 账户标识符)
    • 调用 get_cgrates_action_plans_by_service_uuid(service_uuid)cgrates_service.py
    • 这内部调用 ocs.Get_ActionPlans(service_uuid) 从 CGRateS 获取 ActionPlans
  2. ActionPlan 数据结构 - 返回的每个 ActionPlan 包含:

    {
    "ActionPlanId": "ServiceID_abc-123-def__ProductID_456__...",
    "PlanName": "Monthly_Renewal_Plan",
    "NextExecTime": "2025-02-01T00:00:00Z",
    "custom_NextExecTime_hr": "in 11 days",
    "ActionPlanId_split_dict": {
    "ServiceID": "abc-123-def",
    "ProductID": 456,
    "CustomerID": 789,
    ...
    }
    }
    • ActionPlanId - 包含编码的服务/产品/客户信息的唯一标识符
    • PlanName - 行动计划的名称(通常是续订 playbook 的名称)
    • NextExecTime - ActionPlan 下次执行的 ISO 时间戳
    • custom_NextExecTime_hr - 人类可读的执行时间(例如,“11 天后”、“明天”、“2025 年 2 月 1 日”)
    • ActionPlanId_split_dict - 从 ActionPlanId 中解析的组件字典
  3. UI 显示 - ActionPlans 在 ActionPlansTable 组件中显示:

    表格列:

    • 产品名称 - 通过查找 ActionPlanId 中的 ProductID 检索
    • 成本 - 显示来自产品定义的 retail_cost
    • 续订日期 - 显示 custom_NextExecTime_hr(人类可读)
    • 操作 - 两个按钮:
      • 立即续订 - 立即供应附加组件/续订(绕过等待计划执行)
      • 移除自动续订 - 取消自动续订

    当没有 ActionPlans 存在时:

    • 表格显示消息:“此服务未启用自动续订”
    • 客户可以添���自动续订的附加组件以启用自动续订

管理 ActionPlans

员工和客户可以通过 UI 管理 ActionPlans:

移除 ActionPlan(取消自动续订):

  1. 单击 ActionPlansTable 中的“移除自动续订”按钮
  2. 确认模态出现:“您确定要移除此自动续订吗?”
  3. 确认后,前端调用: DELETE /crm/oam/remove_action_plan/{action_plan_id}
  4. API 通过 ocs.Remove_ActionPlan() 从 CGRateS 中移除 ActionPlan
  5. 活动被记录:“从服务 {service_id} 中移除 ActionPlan {ActionPlanId}”
  6. ActionPlan 从表中消失

立即续订(手动续订):

  1. 单击 ActionPlansTable 中的“立即续订”按钮
  2. 确认模态出现:“您确定要立即续订吗?”
  3. 确认后,系统:
    • 从 ActionPlanId 中提取 product_id
    • 为该产品创建新的供应作业
    • 立即供应附加组件(运行供应 playbook)
    • 服务在等待计划续订的同时接收附加组件的好处
  4. 供应状态模态显示进度
  5. 成功后,余额立即更新

添加自动续订:

通过供应具有设置为 auto_renew 的附加产品来启用自动续订:

  • auto_renew = "true" 的产品 - 在供应期间自动创建 ActionPlans
  • auto_renew = "prompt" 的产品 - 询问客户是否希望自动续订(模态对话框)
  • auto_renew = "false" 的产品 - 永不创建 ActionPlans(一次性购买)

供应 playbook 在 CGRateS 中创建 ActionPlan,内容包括:

  • 编码服务、产品和客户 ID 的唯一 ActionPlanId
  • 续订计划(每月、每年、自定义间隔)
  • 要执行的操作(通常是重新供应相同的附加组件)
  • 到期日期(如果合同具有固定期限)

ActionPlan 命名约定

ActionPlans 遵循标准化的命名约定以编码元数据:

格式:

ServiceID_{service_uuid}__ProductID_{product_id}__CustomerID_{customer_id}__...

示例:

ServiceID_abc-123-def__ProductID_456__CustomerID_789__MonthlyRenewal

此编码允许 CRM:

  • 确定 ActionPlan 属于哪个服务
  • 查找产品详细信息(名称、定价)以进行显示
  • 跟踪客户所有权
  • 解析续订类型和计划

CRM 会自动将这些组件解析为 ActionPlanId_split_dict 以便于访问。

服务视图中的 ActionPlans

在 CRM 中查看服务时,ActionPlans 表格显示在服务详细信息中:

员工视图 (ServiceView.js):

  • 显示完整的 ActionPlans 表格及所有管理选项
  • 显示产品名称、成本、续订日期
  • 允许移除和立即续订

客户自助服务视图

  • 以简化视图显示即将到来的续订
  • 显示下一个续订日期和金额
  • 可能允许客户禁用自动续订(按产品可配置)

空状态

  • 如果没有 ActionPlans 存在:“此服务未启用自动续订”
  • 建议添加自动续订的附加组件以启用自动续订

ActionPlans 和即将到期的服务

CRM 包含一个即将到期的服务端点,以识别需要续订的服务:

GET /crm/service/expiring?threshold=7

这将返回在阈值天数内到期的服务,包括:

  • 即将到期的余额的服务
  • 没有活动 ActionPlans 的服务(需要手动续订)
  • 即将执行的 ActionPlans 的服务

这允许操作员:

  • 向客户发送续订提醒
  • 识别面临风险的客户(即将到期而没有自动续订)
  • 监控即将到来的自动续订
  • 主动管理服务连续性