CRM 服务 / 产品计费说明
::: note ::: title 注意 :::
有关产品定义、供应、附加组件和去供应的完整端到端指南,以及详细的 Ansible 示例和定价策略,请参见 完整产品生命周期指南。 :::
产品与服务概述
产品(菜单项):
产品就像餐厅菜单上的特定菜肴,例如“意大利培根意面”。
它有明确的描述、成分列表(如意大利面、奶油、鸡蛋、奶酪和培根)和价格。
在 OmniCRM 中,产品同样包含了所包含内容的详细信息——功能、规格和定价。
通常,客户可能希望进行修改,比如“去掉洋葱”或“加点额外的奶酪”。在 OmniCRM 中,这对应于在创建之前自定义服务。对服务的自定义或修改的程度由您(操作员)定义。
在 OmniCRM 中,客户或员工可能会修改产品,以更好地满足特定客户的需求,例如升级他们的互联网速度或添加特定功能。这种自定义反映在提供的特定服务中。
产品本质上是客户可以选择订购的产品,��似于从菜单上阅读和选择菜肴。

产品目录(餐厅菜单):
产品目录就像餐厅的整个菜单,列出了所有可用的菜肴——从开胃菜到甜点。
它是餐厅(或在您的情况下,服务提供商)提供的所有内容的完整集合。
在商业环境中,产品目录为客户提供所有可用产品,以便他们可以选择最符合其需求的产品。


服务(准备好的菜肴):
当客户从菜单上点菜时,菜肴在厨房中准备。这类似于从产品创建服务。
在 OmniCRM 中,当客户选择产品时,会创建该产品的一个实例并作为服务交付。
它是专门为该客户定制和准备的,就像为就餐者准备的餐点。
例如,当某人从产品目录中选择“互联网铜牌计划”时,供应系统会“烹饪”出该计划的一个实例,所需的成分(IP 地址、调制解调器和端口)——即激活该计划并将其交付给特定客户。
捆绑产品(组合餐):
产品目录还可能提供捆绑,例如组合餐,其中包括开胃菜、主菜和甜点,以特价一起提供。
在 OmniCRM 中,捆���产品将多个单独的产品组合成一个方便的包——例如“家庭必需品捆绑”,包括互联网、有线电视和电话服务,享受折扣价格。
一旦选择,这个捆绑就会转化为多个为客户量身定制的服务。
产品定义
产品是用于创建服务/附加组件/折扣/附加功能等的模板。
在定义中,我们包括:
-
关于产品的信息(功能、包含内容、条款和条件、合同期限、图标等),这些信息会显示给 CRM 的用户(客户或 员工)。
-
关于谁可以购买该产品的业务逻辑(商业或住宅),它是否依赖于已供应的父服务(例如,仅对拥有移动服务的客户可用的移动附加组件),它是否可以通过自助服务直接由客户订购,或者仅由客户服务代理订购,以及何时可以购买该产品(允许产品仅在设定的时间段内可用)。
-
当要包含库存项目(例如调制解调器或 SIM 卡)时,这些项目会被指定为库存项目列表,例如以下服务需要分配一个 SIM 卡和一个电话号码:
['SIM Card', 'Phone Number']这些与 CRM 中定义的 库存项目 相关联。 -
引用 Ansible Playbook 以供应服务 供应 Play 以及要传递��� Ansible 的变量。这些要传递的变量是魔法,因为它们可能是由我们添加的产品定义的变量,如
service_id,或者它们可能是像ICCID和MSISDN这样的变量,我们在分配库存时选择了库存项目。捆绑在供应 Play 中处理,以包含多个服务,例如捆绑的家庭互联网、电视和语音产品,可能为每个服务提供一个服务。

产品类别和服务类型
产品使用两个分类字段来帮助组织和过滤产品:
产品类别
category 字段控制产品在 UI 中的显示位置。
常见值包括:
- standalone - 在创建新服务时显示为基础服务选项
- addon - 在添加到现有服务时显示
- bundle - 显示为捆绑服务选项(像附加组件一样供应给现有服务)
- promo - 特别促销产品
这些类别纯粹是组织性的,不决定什么被供应。实际的供应行为完全由 provisioning_play 中引用的 Ansible playbook 决定。
例如:- standalone 产品通常创建一个新的服务对象 - addon 或 bundle 产品通常添加到现有服务 - 但这取决于编写 playbook 的实施者 - 您可以从附加组件创建多个服务对象,或者根据需要从独立产品修改现有服务
类���仅控制 UI 流程以及客户/员工看到产品选项的位置。
服务类型
service_type 字段对提供的服务类型进行分类。
这些完全由用户定义,但常见值包括:
- mobile - 具有语音、短信和数据的移动电话服务
- fixed - 固定无线或有线互联网服务
- fixed-voice - 固定电话语音服务(VoIP、座机)
- hotspot - 移动热点或租赁设备
- dongle - USB 调制解调器或加密狗服务
- voice - 仅语音服务
- data - 仅数据服务
与类别一样,服务类型可以根据您的产品进行自定义。它们有助于:
- 过滤哪些附加组件适用于哪些基础服务
- 在客户门户中组织产品
- 匹配库存要求
- 确定供应工作流
示例:拥有 mobile 服务的客户可以看到移动附加组件,而拥有 fixed 服务的客户可以看到固定线路附加组件。
管理产品
产品通过产品管理页面进行管理,您可以在此查看、搜索、过滤和编辑所有可用产品。
{.align-center
width="800px"}
产品模态界面
单击任何产品会打开一个增强的选项卡界面,将所有产品设置组织成逻辑组,以便更轻松地导航和编辑。
{.align-center
width="800px"}
产品管理模态具有五个组织良好的选项卡:
- 📋 基本信息 - 核心产品信息(名称、别名、类别、图标、功能、条款)
- 💰 定价 - 所有与成本相关的字段,包括经常性成本、设置成本和税率
- ⚙️ 配置 - 续订设置、客户类型和依赖关系
- 🔧 供应 - Ansible playbook 配置和库存要求
- 📅 可用性 - 日期范围和系统时间戳
{.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_id、product_id、service_id和access_token。 - inventory_items_list - 此产品所需的库存项目列表(例如,
['SIM Card', 'Mobile Number'])。当客户下订单时,他们将被提示从可用库存中选择特定项目。选定的库存 ID 作为变量名称传递给供应 playbook。 - relies_on_list - 在添加此产品之前必须存在的产品 ID 或服务类型列表。用于附加组件依赖关系(例如,移动附加组件需要活动的移动服务)。
系统元数据
- created - 产品创建的时间戳(自动设置)
- last_modified - 产品最后更新的时间戳(自动更新)
示例产品定义
独立产品(移动 SIM)

{
"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 至关重要。
变量来源和合并
当创建供应作业时,变量来自多个来源,并按以下顺序合并(后来的来源覆盖较早的来源):
- 产品的 provisioning_json_vars - 来自产品定义的默认变量
- 请求体 - 在 API 调用中传递的变量(可以覆盖产品默认值)
- 系统添加的变量 - 由供应系统自动添加
- 库存选择 - 选定库存项目的 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_id、customer_id、service_id等)
库存变量
当产品需要库存项目时(例如,inventory_items_list: "['SIM Card', 'Mobile Number']"),过程如下:
- UI/API 提示选择 - 用户从可用库存中选择特定库存项目
- 库存 ID 被添加到变量中 - 选定的库存项目 ID 被添加,库存类型作为变量名称
- 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- 正在供应的附加产品的 IDcustomer_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 使用这些变量来:
- 从 CRM API 获取服务详细信息,使用
service_id - 从服务记录中提取服务 UUID 和其他信息
- 向��费系统(OCS)添加信用,使用服务 UUID
- 在 CRM 中记录交易 以便计费
此流程允许附加组件准确识别要修改的服务并适当地应用更改。
独立产品与附加产品变量的区别
独立产品接收:
product_id- 正在供应的产品customer_id- 订购服务的客户- 如果产品需要库存项目,则包括库存项目 ID(例如,
SIM Card、Mobile 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 根据以下条件过滤产品:
- 客户类型 - 产品被分类为:
- 个人(住宅):
residential = true或business = false的产品 - 商业:
business = true的产品
- 个人(住宅):
- 类别 - 产品分为:
- 服务计划:
category=standalone或bundle的产品 - 附加组件:
category=addon的产品(在单独的轮播中显示)
- 服务计划:
- 可用性 - 仅在以下情况下显示产品:
enabled = true- 产品处于活动状态且未禁用- 当前日期在
available_from和available_until之间 - 产品���其可用性窗口内 customer_can_purchase = true(如果客户是自助购买) - 产品允许直接客户购买
::: note ::: title 注意 :::
API 级过滤:API 自动按启用状态和可用日期在两个级别过滤产品:
- 购买/选择端点 (
/crm/product/) - 由附加组件模态和计划列表用于产品选择。自动过滤以仅显示在其可用日期范围内启用的产品。这确保客户和员工只能选择当前可供购买的产品。 - 管理端点 (
/crm/product/paginated) - 由产品管理页面使用。显示所有产品,包括禁用和超出可用日期的产品,允许管理员管理完整的产品目录,包括非活动产品。
将 include_disabled=true 传递给基础产品端点以绕过过滤(仅供管理使用)。
:::
UI 为以下内容显示单独的轮播:
- 个人服务计划 - 住宅客户的产品
- 商业服务计划 - 商业客户的产品
- 个人附加组件 - 住宅附加包
- 商业附加组件 - 商业附加包
附加服务的产品过滤
在向 现有服务 添加附加组件时,应用额外的过滤:
- 服务类型匹配 - 仅显示匹配的
service_type的附加组件:- 如果现有服务的
service_type = "mobile",则仅显示service_type = "mobile"的附加组件 - 这确保移动客户仅看到移动附加组件,互联网客户仅看到互联网附加组件,等等。
- 如果现有服务的
- 依赖关系检查 - 如果附加组件具有
relies_on_list:- 系统检查客户是否拥有所需的产品/服务
- 仅显示满足其依赖关系的附加组件
- 相同客户类型过滤 - 附加组件仍按
residential与business过滤,以匹配客户类型
示例过滤场景
对于拥有现有移动服务的商业客户(service_type = "mobile"):
- 显示的独立产品:所有商业独立/捆绑产品(
business = true,category != "addon") - 显示的附加产品:仅商业移动附加组件(
business = true,category = "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。
高级供应流程
- 预供应设置 - 在 API 中创建产品并配置供应,并编写和测试相应的 Ansible playbooks
- 服务选择 - 从客户页面,员工或客户选择“添加服务”
- 产品过滤 - 显示的产品根据以下条件过滤:
- 客户类型(住宅/商业)
- 现有服务(用于附加组件依赖关系中的
relies_on_list) - 可用日期(
available_from/available_until) enabled和customer_can_purchase标志
- 自定义 - 选项覆盖供应变量(用于价格调整、自定义配置等)
- 库存选择 - 如果产品需要库存(
inventory_items_list不为空),用户选择特定项目(例如,选择哪个 SIM 卡、哪个电话号码) - 供应启动 - 当单击“供应”按钮时,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- 订购服务的客户 IDproduct_id- 正在供应的产品 IDservice_id- (可选)现有服务的 ID,用于附加组件- 库存选择 - 选定库存项目的 ID
步骤 2:变量组装 (services/provisioning_service.py)
供应服务从多个来源按以下顺序合并变量:
- 产品的
provisioning_json_vars(来自产品定义的默认值) - 请求体参数(可以覆盖产品默认值)
- 系统添加的变量:
access_token- 用于 API 身份验证回 CRM 的 JWT 令牌initiating_user- 触发供应的用户 IDcustomer_id、product_id、service_id
- 库存选择 - 作为
{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_id、customer_id、service_id- 上下文引用
步骤 4:后台 Playbook 执行
(Provisioners/playbook_runner_v2.py)
API 启��一个后台线程:
- 从
OmniCRM-API/Provisioners/plays/{playbook_name}.yaml加载 playbook YAML - 调用
ansible_runner.run(),参数包括:playbook- 加载的 YAML 文件的路径extravars- 所有合并的变量(传递给 Ansible)inventory- 设置为'localhost,'(本地执行)event_handler- 自定义处理程序以捕获任务执行事件
- 实时监控 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_id和product_id - 通常需要库存项目(SIM 卡、电话号码、���制解调器)
- 通过 API PUT
/crm/service/创建 新 服务记录 - 在外部系统上供应新资源(HSS、OCS、网络设备)
- 示例:新移动 SIM 激活、新互联网连接
附加产品(category: addon):
- 接收
customer_id、product_id和service_id(要修改的现有服务) - 通常不需要库存(或仅需少量库存)
- 修改现有 服务或向现有 OCS 账户添加费用
- 可能在 OCS 上执行操作(添加数据包、添加信用、启用功能)
- 不创建新服务记录(或创建与父服务关联的子服务记录)
- 示例:每月数据计划充值、国际漫游包、额外信用
捆绑产品(category: bundle):
- 在接收的变量方面与附加产品类似
- 可能需要一些库存项目(例如,家庭捆绑的调制解调器)
- 创建 多个 相关服务记录(互联网 + 电视 + 电话)
- 在不同系统上供应多个资源
- 在 CRM 中将服务链接在一起以实现统一计费/管理
- 示例:家庭捆绑(互联网 + IPTV + VoIP 电话)
供应 playbook 要求
为了使 playbook 正常工作,它必须:
- 位于
OmniCRM-API/Provisioners/plays/{playbook_name}.yaml - 通过 Ansible 的
extravars接受变量(通过{{ variable_name }}访问) - 使用
Authorization: Bearer {{ access_token }}头进行 API 调用身份验证 - 优雅地处理失败,在适当的地方使用
rescue块和ignore_errors - 为独立产品创建服务记录,或修改附加产品的现有服务
- 如果选择了库存,则分配库存
- 在发生关键错误时通过
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 流程
对于移动数据充值附加组件:
- Playbook 接收:
customer_id、product_id、service_id、access_token - 获取服务详细信息:
GET /crm/service/{{ service_id }} - 获取产品详细信息:
GET /crm/product/product_id/{{ product_id }} - 在 OCS 中向客户收费:
POST to CGRateS从余额中扣除 retail_cost - 在 OCS 中添加数据信用:
POST to CGRateS添加到期的数据余额 - 在 CRM 中记录交易:
POST /crm/transaction/以获取收费详细信息 - 成功完成
整个过程在 Provision 和 Provision_Event 表中跟踪,以便于调试和审计。
OCS 参与
OCS(在线计费系统),通过 CGRateS 实现,处理所有实时计费和服务使用跟踪。CRM 服务记录充当指向 OCS 账户的指针,后者管理:
- 经常性费用 - 每月费用、DID 租赁、订阅费用
- 基于使用的计费 - 每分钟语音通话、每 MB 数据、每条短信费用
- 余额管理 - 货币余额(预付信用)和单位余额(数据 GB、语音分钟、短信计数)
- 余额转换 - 将货币���额转换为单位余额(例如,花费 $30 获取 10GB 数据包)
- 账户状态 - 根据信用限额和阈值的活动、暂停、禁用
CRM 服务记录包含元数据和配置(客户、产品、定价、可见性),而 OCS 包含实时计费状态(余额、使用情况、费用)。
检索服务使用情况和余额
服务使用信息从 OCS/CGRateS 检索,并实时显示给客户和员工。
如何检索使用情况
当请求服务的使用情况时(通过 UI 或 API),发生以下流程:
-
API 请求 - 前端调用
GET /crm/service/{service_id}或在 UI 中查看服务详细信息 -
服务查找 - API 从数据库中检索服务记录,提取
service_uuid -
CGRateS API 调用 -
cgrates_service.py模块向 CGRateS 发出两个调用:a. Get_Balance(service_uuid) - 检索账户余额,使用
BalanceMap- 返回按类型组织的余额:数据、语音、短信、货币、数据加密狗
- 每个余额包括:ID、值、到期日期、权重、目标 ID
- 系统添加人类可读字段:
custom_Name_hr、custom_Expiration、custom_Description_Stringb. Get_ActionPlans(service_uuid) - 检索活动的自动续订行动计划(在下一节中介绍)
-
响应合并 - 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": [...]
}
} -
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
- 将货币余额重置为 $0
- 向 CRM 的
/simple_provision发送 HTTP POST 请求以供应某些内容 - 添加 1TB 使用量的信用,过期时间为 1 个月
如果我们希望 MRC 是经常性的(我们希望),那么我们将创建一个名为 ActionPlan_{{ service_uuid }}_Monthly_Charge 的 ActionPlan,其时间设置为 每月 以每月触发,并将 ActionPlan 分配给账户。
我们可以根据 年/月/天 参数设置到期日期,以便在此点停止 MRC,例如对于固定的 12 个月服务。
由于操作和 ActionPlans 对每项服务都是唯一的,因此它们不会与其他服务共享任何内容。
这意味着我们可以使用调整后的值进行供应,而不会影响其他服务。
附加组件和附加功能
附加组件/附加功能,例如购买额外数据、漫游包、国际分钟等,处理方式与此类似。创建一个操作以执行所需的操作,例如收取货币值,然后授予具有设定到期的单位余额。
我们不是使用 ActionPlans 自动将其添加到账户,而是从 Ansible 中一次性触发我们刚刚创建的 ExecuteAction。
添加预付货币余额
对于预付货币余额,例如 $10 的 PAYG 计划,这被添加为货币余额,但优先级更高。
这些服务的默认余额的信用限额将为 $0。
信用限额 / 防止过度支出
ThresholdS 用于每个账户设置给定时间段的最大支出。
对于 PAYG / 预付客户,这为 $0。
通过 CRM 与 OCS 交互
对于每项服务,您可以在 CRM API 中查看 Balances 和 ActionPlans、Actions 和 ThresholdS。
可以根据需要从 CRM API 中删除 ActionPlans,通过 Ansible Playbooks 执行。可以根据需要从 CRM 中添加 ActionPlans,通过添加附加组件/服务并通过 Ansible Playbooks 执行。
可以禁用 OCS 账户,这将停止 ActionPlans 的执行和服务的消费。
对于信用限额,按产品政策设置 ThresholdS 值。
在 CRM 中查看和管理 ActionPlans
ActionPlans(自动续订配置)通过 CRM 界面显示和管理,允许员工和客户查看即将到来的自动续订并进行管理。
如何检索和显示 ActionPlans
在 CRM 中查看服务时,ActionPlans 会自动获取并显示:
-
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
-
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 中解析的组件字典
-
UI 显示 - ActionPlans 在 ActionPlansTable 组件中显示:
表格列:
- 产品名称 - 通过查找 ActionPlanId 中的
ProductID检索 - 成本 - 显示来自产品定义的
retail_cost - 续订日期 - 显示
custom_NextExecTime_hr(人类可读) - 操作 - 两个按钮:
- 立即续订 - 立即供应附加组件/续订(绕过等待计划执行)
- 移除自动续订 - 取消自动续订
当没有 ActionPlans 存在时:
- 表格显示消息:“此服务未启用自动续订”
- 客户可以添���自动续订的附加组件以启用自动续订
- 产品名称 - 通过查找 ActionPlanId 中的
管理 ActionPlans
员工和客户可以通过 UI 管理 ActionPlans:
移除 ActionPlan(取消自动续订):
- 单击 ActionPlansTable 中的“移除自动续订”按钮
- 确认模态出现:“您确定要移除此自动续订吗?”
- 确认后,前端调用:
DELETE /crm/oam/remove_action_plan/{action_plan_id} - API 通过
ocs.Remove_ActionPlan()从 CGRateS 中移除 ActionPlan - 活动被记录:“从服务 {service_id} 中移除 ActionPlan {ActionPlanId}”
- ActionPlan 从表中消失
立即续订(手动续订):
- 单击 ActionPlansTable 中的“立即续订”按钮
- 确认模态出现:“您确定要立即续订吗?”
- 确认后,系统:
- 从 ActionPlanId 中提取 product_id
- 为该产品创建新的供应作业
- 立即供应附加组件(运行供应 playbook)
- 服务在等待计划续订的同时接收附加组件的好处
- 供应状态模态显示进度
- 成功后,余额立即更新
添加自动续订:
通过供应具有设置为 auto_renew 的附加产品来启用自动续订:
auto_renew = "true"的产品 - 在供应期间自动创建 ActionPlansauto_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 的服务
这允许操作员:
- 向客户发送续订提醒
- 识别面临风险的客户(即将到期而没有自动续订)
- 监控即将到来的自动续订
- 主动管理服务连续性