完整产品生命周期指南
本指南提供了在 OmniCRM 中产品生命周期的端到端流程,从创建产品定义到服务配置、添加附加组件和取消配置。我们将涵盖定价策略、Ansible 集成,并在整个过程中提供实际案例。
概述:产品到服务的旅程
OmniCRM 中产品的生命周期遵循以下阶段:
- 产品定义 - 管理员创建包含定价和配置规则的产品模板
- 服务创建 - 客户订购产品,系统配置服务实例
- 服务生命周期 - 客户使用服务,添加附加组件/充值,修改服务
- 取消配置 - 服务被终止,资源被释放
理解定价:批发与零售
OmniCRM 中的每个产品和服务都有两个定价维度:批发和零售。
批发成本
批发成本代表交付服务的实际成本:
- 基础设施和带宽成本
- 许可费用
- 设备成本
- 运营费用
零售成本
零售成本是向客户收取的金额。
设置成本
批发和零售都有一次性配置费用的变体:
wholesale_setup_cost- 您的配置成本retail_setup_cost- 向客户收取的激活费用
示例:
{
"retail_cost": 15.00,
"wholesale_cost": 5.00,
"retail_setup_cost": 0.00,
"wholesale_setup_cost": 1.00
}
阶段 1:创建产品定义
产品是定义要配置的内容及客户如何收费的模板。
创建移动 SIM 产品
让我们创建一个每月 20GB 数据的预付费移动 SIM 产品。
步骤 1:导航到产品管理
从管理员 UI,转到 产品 → 创建产品。
步骤 2:定义基本信息
{
"product_name": "Prepaid Mobile 20GB",
"product_slug": "prepaid-mobile-20gb",
"category": "standalone",
"service_type": "mobile",
"enabled": true,
"icon": "fa-solid fa-sim-card",
"comment": "预付费移动 SIM,包含 20GB 数据,无限通话和短信"
}
字段说明:
product_name- 客户可见的名称,在目录中显示product_slug- 在 API 调用和链接中使用的 URL 安全标识符category- “standalone” 意味着这创建了一个新服务(与附加组件/捆绑不同)service_type- 将相关产品分组,用于附加组件过滤enabled- 产品必须为 true 才能被订购icon- 在 UI 中显示的 FontAwesome 图标comment- 内部备注供员工参考
步骤 3:设置定价
{
"retail_cost": 15.00,
"wholesale_cost": 5.00,
"retail_setup_cost": 0.00,
"wholesale_setup_cost": 1.00,
"contract_days": 30
}
定价细分:
- 每位客户的月收入:£15.00
- 每月交付成本:£5.00
- 每月利润率:£10.00(200% 加价,67% 利润)
- 设置利润:-£1.00(补贴以吸引客户)
- 合同期限:30 天(每月续订)
步骤 4:定义客户资格
{
"residential": true,
"business": false,
"customer_can_purchase": true,
"available_from": "2025-01-01T00:00:00Z",
"available_until": null
}
- 住宅客户可以订购
- 商业客户不能(不同的产品线)
- 自助购买已启用
- 从 2025 年 1 月 1 日起可用
- 没有结束日期(持续优惠)
步骤 5:配置自动续订
{
"auto_renew": "prompt",
"allow_auto_renew": true
}
"prompt"- 在购买时询问客户是否希望自动续订"true"- 自动续订,无需询问"false"- 永不自动续订(仅手动充值)allow_auto_renew: true- 客户可以稍后启用/禁用自动续订
步骤 6:指定库存要求
库存要求定义在配置此产品时必须���配哪些物理或虚拟资源。这是将您的产品目录与您的 库存管理系统 连接起来的关键步骤。
{
"inventory_items_list": "['SIM Card', 'Mobile Number']"
}
什么是库存项目?
库存项目是存储在 OmniCRM 库存系统中的可追踪资源。每个项目都有:
- 类型 - 由库存模板定义(例如,“SIM 卡”、“手机号码”、“调制解调器”)
- 唯一属性 - 序列号、MAC 地址、电话号码等
- 状态 - 在库、已分配、退役等
- 位置 - 物理或逻辑位置
库存要求如何工作:
inventory_items_list 是一个包含库存类型名称的 Python 列表(作为字符串)。每个名称必须完全匹配现有的
库存模板 名称。
示例库存要求:
# 移动 SIM 产品
inventory_items_list: "['SIM Card', 'Mobile Number']"
# 固定互联网服务
inventory_items_list: "['Modem Router', 'Static IP Address']"
# 数字服务(没有物理项目)
inventory_items_list: "[]"
# 固定无线与 CPE
inventory_items_list: "['Fixed Wireless CPE', 'IPv4 Address', 'IPv6 Prefix']"
库存选择器流程
当用户配置具有库存要求的产品时,系统强制执行强制选择流程:
1. 点击配置按钮
选择产品后,用户点击“配置”。系统检查 inventory_items_list,而不是立即配置。
2. 库存选择器模态框出现
如果需要库存,将出现一个模态对话框,每种库存类型都有一个单独的下拉菜单:
3. 过滤可用库存
每种库存类型的下拉菜单仅显示以下项目:
- 正确类型 - 完全匹配库存模板名称
- 可用状态 -
item_state为“新”或“在库”(而不是“已分配”或“损坏”) - 未分配 -
service_id和customer_id为 NULL - 在指定位置有库存 - 可选择按仓库/商店位置过滤
示例下拉选项:
对于“SIM 卡”库存类型,下拉菜单可能显示:
每个选项显示:
- 库存 ID 或参考编号
- 主要标识符(
itemtext1- 例如,SIM 的 ICCID,电话的号码) - 当前地点(
item_location)
4. 选择是继续的必要条件
关键规则: 在选择所有必需的库存项目之前,配置无法继续。
- “继续”按钮在所有下拉菜单都有选择之前被禁用
- 用户必须为每种库存类型选择一个项目
- 系统在继续之前验证选择
5. 选定的库存传递给 Ansible
一旦用户点击“继续”,所选库存 ID 作为变量传递给 Ansible 剧本:
# 用户选择:
# - SIM 卡 inventory_id: 5001
# - 手机号码 inventory_id: 5002
# 传递给 Ansible 的变量:
{
"product_id": 42,
"customer_id": 123,
"SIM Card": 5001, # 库存 ID
"Mobile Number": 5002, # 库存 ID
"access_token": "eyJ..."
}
注意: 变量名称与库存类型完全匹配。剧本使用 hostvars[inventory_hostname]['SIM Card'] 来访问库存 ID。
6. 剧本获取完整库存详细信息
Ansible 剧本使用库存 ID 获取完整详细信息:
- name: 从库存获取 SIM 卡详细信息
uri:
url: "{{ crm_config.crm.base_url }}/crm/inventory/inventory_id/{{ hostvars[inventory_hostname]['SIM Card'] }}"
method: GET
headers:
Authorization: "Bearer {{ access_token }}"
register: api_response_sim
- name: 提取 ICCID 和 IMSI
set_fact:
iccid: "{{ api_response_sim.json.itemtext1 }}"
imsi: "{{ api_response_sim.json.itemtext2 }}"
现在剧本拥有所有 SIM 详细信息(ICCID、IMSI 等)以在 HSS 中配置用户。
7. 库存状态更改为“已分配”
在创建服务记录后,剧本更新库存以将其链接到服务:
- name: 将 SIM 卡分配给服务
uri:
url: "{{ crm_config.crm.base_url }}/crm/inventory/inventory_id/{{ hostvars[inventory_hostname]['SIM Card'] }}"
method: PATCH
body:
{
"service_id": "{{ service_creation_response.json.service_id }}",
"customer_id": "{{ customer_id }}",
"item_state": "Assigned"
}
重要: 库存分配发生在 剧本执行期间 作为特定任务,而不是在点击配置按钮时。这意味着:
- 双重分配风险:在点击“配置”和库存被分配之间,另一个用户理论上可以选择同一库存项目
- 最佳实践:对于高容量操作,实施库存锁定或使用数据库事务
- 失败时回滚:如果剧本在库存分配之前失败,库存保持未分配状态并可供重用
为什么不早点分配?
在点击“配置”时不会分配库存,因为:
- 需要服务 ID:
service_id在剧本中创建服务之前不存在 - 回滚简单性:如果配置早期失败(例如,OCS 账户创建失败),则不需要清理库存
- 灵活性:剧本可以根据条件逻辑决定不分配库存
处理失败的配置:
当配置在库存分配后失败时,救援块应释放库存:
rescue:
- name: 失败时释放库存
uri:
url: "{{ crm_config.crm.base_url }}/crm/inventory/inventory_id/{{ hostvars[inventory_hostname]['SIM Card'] }}"
method: PATCH
body:
{
"service_id": null,
"customer_id": null,
"item_state": "In Stock"
}
when: service_id is defined # 仅在服务已创建时
这确保库存不会留在“已分配”状态下,针对不存在或失败的服务。
当库存列表为空时
如果 inventory_items_list: "[]"(空列表),则完全跳过库存选择器,配置立即进行。这在以下情况下很常见:
- 数字产品 - 软件许可证、VPN 账户
- 服务附加组件 - 不需要新硬件的数据充值
- 虚拟服务 - 不消耗可追踪资源
示例: “5GB 数据提升”附加组件具有 inventory_items_list: "[]",因为它只是向现有服务添加余额,而不需要新硬件。
库存模板设置
在 inventory_items_list 中使用库存类型之前,您必须创建库存模板:
- 导航到 管理 → 库存 → 模板
- 创建具有确切名称的模板(例如,“SIM 卡”)
- 定义字段:
itemtext1_label: "ICCID"itemtext2_label: "IMSI"itemtext3_label: "PUK 代码"
- 将此类型的库存项目添加到库存中
有关创建和管理库存模板的完整详细信息,请参见 库存管理。
相同类型的多个项目
虽然 inventory_items_list 是一个数组,但拥有重复类型(例如,"['SIM Card', 'SIM Card']")不推荐,因为这可能会导致 UI 和剧本变量命名中的混淆。
对于需要多个相似项目的场景:
选项 1:创建不同的库存模板名称
# 双 SIM 手机服务
inventory_items_list: "['Primary SIM Card', 'Secondary SIM Card', 'Mobile Number']"
创建单独的模板:“Primary SIM Card”和“Secondary SIM Card”,具有相同字段但不同名称。
选项 2:使用单个捆绑库存项目
# 双 SIM 套件
inventory_items_list: "['Dual SIM Kit', 'Mobile Number']"
其中“Dual SIM Kit”库存模板具有两个 SIM 的字段(itemtext1: 主 ICCID,itemtext2: 次 ICCID 等)。
常见库存场景
移动服务:
inventory_items_list: "['SIM Card', 'Mobile Number']"
- SIM 卡:带有 ICCID/IMSI 的物理或 eSIM
- 手机号码:电话号码(MSISDN)
固定互联网:
inventory_items_list: "['Modem Router', 'Static IP Address']"
- 调制解调器路由器:带有 MAC 地址的 CPE 设备
- 静态 IP 地址:来自地址池的 IPv4
固定无线:
inventory_items_list: "['Fixed Wireless CPE', 'IPv4 Address', 'IPv6 Prefix']"
- CPE:客户场所设备(天线、调制解调器)
- IPv4:公共 IP 地址
- IPv6 前缀:/56 或 /64 前缀
注意: 预约和调度不是库存项目。请使用单独的调度/日历系统进行安装预约。
VoIP 服务:
inventory_items_list: "['DID Number']"
- DID 号码:直接拨入电话号码
注意: SIP 用户名、密码和账户配置由配置剧本程序生成,而不是从库存中选择。
GPON/光纤:
inventory_items_list: "['ONT Device', 'GPON Port', 'IPv4 Address', 'Fiber Drop Cable']"
- ONT 设备:带有序列号的光网络终端
- GPON 端口:与光纤连接的 OLT 上的特定端口
- IPv4 地址:公共或私有 IP
- 光纤下线电缆:从街道到场所的物理光纤电缆(用于资产管理跟踪)
设备租赁:
inventory_items_list: "['Rental Modem']"
- 跟踪哪个调制解调器与哪个客户
- 取消时回收设备非常重要
为什么库存要求很重要
1. 防止双重分配
没有库存跟踪,您可能会意外地:
- 将同一 SIM 卡分配给两个客户
- 将同一 IP 地址��配给多个服务
- 将同一设备序列号发货到不同位置
库存选择器确保每个项目仅分配给一个服务。
2. 审计跟踪
库存分配创建完整的审计跟踪:
- 哪个 SIM 卡与哪个客户
- 何时分配
- 哪个服务使用哪个电话号码
- 设备历史(谁拥有,何时,服务是什么)
3. 资源规划
跟踪库存水平:
- 当 SIM 卡即将用完时发出警报
- 在缺货之前重新订购
- 根据 CPE 可用性规划技术人员日程
- 管理 IP 地址空间分配
4. 成本跟踪
将批发成本链接到特定项目:
- 跟踪每个 SIM 卡的成本
- 计算设备折旧
- 确定丢失或被盗的项目
- 准确的 COGS(销售成本)
5. 取消配置
当服务被取消时,库存可以:
- 释放回库存(SIM 卡、调制解调器)
- 退役(损坏的设备)
- 退还给供应商(租赁设备)
- 在宽限期内保留(释放前的电话号码)
排查库存选择器问题
问题: “没有可用库存”消息出现
原因:
- 数据库中不存在所需类型的库存项目
- 所有项目已“分配”给其他服务
- 项目被标记为“损坏”或“停用”
- 库存模板名称不完全匹配(区分大小写)
解决方案:
- 验证库存模板是否存在:管理 → 库存 → 模板
- 检查模板名称是否完全匹配(包括空格、大小写)
- 添加此类型的库存项目:管理 → 库存 → 添加项目
- 验证项目是否处于“新”或“在库”状态
- 检查项目是否未分配(
service_id应为 NULL)
问题: 库存选择器未出现
原因:
inventory_items_list为空:"[]"inventory_items_list为 NULL 或未设置- 产品类别为“附加组件”,并继承父服务库存
解决方案:
- 如果需要库存,请设置
inventory_items_list: "['Type1', 'Type2']" - 验证产品定义是否正确保存
- 检查 API 响应以确保产品包含 inventory_items_list
问题: 剧本失败,出现“未找到库存”
原因:
- 剧本引用了错误的变量名称
- 库存 ID 未正确传递
- 在选择和配置之间库存已被删除
解决方案:
- 验证剧本使用正确的变量:
hostvars[inventory_hostname]['SIM Card'] - 检查变量是否为整数:
{{ hostvars[inventory_hostname]['SIM Card'] | int }} - 在剧本中添加缺失库存的错误处理
请参见 库存管理 以获取有关创建模板、添加项目��管理库存水平的完整详细信息。
步骤 7:定义功能和条款
功能和条款是面向客户的营销和法律内容,帮助客户理解他们所购买的内容及相关义务。
{
"features_list": "20GB 高速数据。无限通话和短信。包括欧盟漫游。无合同。30天到期",
"terms": "信用在 30 天后到期。数据、通话和短信仅在到期期间有效。适用公平使用政策。请参阅网站以获取完整条款。"
}
目的和商业价值
功能列表 - 营销与销售:
功能列表服务多个关键业务功能:
- 产品差异化 - 帮助客户快速比较产品并选择合适的产品
- “预付费移动 20GB”与“预付费移动 50GB” - 功能清楚地显示差异
- 如果没有功能,客户只会看到价格,错过价值主张
- 营销沟通 - 关键卖点显著展示
- “包括欧盟漫游”吸引国际旅行者
- “无合同”吸引不愿意承诺的客户
- 功能推动购买决策
- 客户期望 - 明确设定包含的内容
- 减少支持电话(“这包括通话吗?”→ 清楚列出)
- 防止误解和退款请求
- 通过透明度建立信任
- 自助服务 - 使客户能够自选适当的产品
- 客户阅读功能,理解提供,做出明智选择
- 减少销售人员的解释需求
- 加快购买过程
- SEO 和可发现性 - 功能可以被索引以供搜索
- 客户搜索“无限通话移动计划”→ 产品出现
- 提高产品目录的可搜索性
条款和条件 - 法律与合规:
条款服务法律和运营目的:
- 法律保护 - 保护企业免受争议和责任
- “信用在 30 天后到期” - 客户不能在 31 天时要求退款
- “适用公平使用政策” - 防止滥用(在移动计划上为整个办公室提供网络)
- 创建具有约束力的协议
- 期望管理 - 防止客户不满
- “仅在到期期间有效” - 客户知道使用截止日期
- “不可退款”(对于附加组件) - 防止欺诈性购买
- 减少退款和投诉
- 合规性 - 符合法律要求
- 消费者保护法要求明确条款
- 电信法规要求披露
- GDPR/隐私条款可以被引用
- 运营边界 - 定义服务范围和限制
- “受网络覆盖限制” - 不对死区负责
- “速度可能会有所不同” - 管理对“最高”速度的期望
- “设备必须归还” - 确保租赁设备的回收
- 审计跟踪 - 证明客户已被告知
- 客户在购买时接受条款
- 系统记录接受时间戳
- 在争议或法律程序中可辩护
实际案例:
客户购买“无限通话和短信”计划,然后用于电话营销(每天 10,000 个电话)。没有条款:
- 客户:“你说无限!”
- 提供商:“我们的意思是个人使用...\”
- 客户:“这不是你宣传的内容!”
- 结果:争议,潜在的监管投诉,品牌损害
有条款:“适用公平使用政策。服务仅供个人使用。禁止商业使用。”
- 提供商:指向客户接受的条款
- 客户无法声称无知
- 有法律依据暂停服务
- 争议以提供商的有利结果解决
功能列表格式:
理解正确的格式至关重要,因为不正确的格式会破坏 UI 显示。功能可能会显示为一个长字符串而不是项目符号,或者根本不显示。
features_list 字段可以以两种方式格式化:
选项 1:以句号分隔的字符串(推荐)
功能用句号和空格(“.”)分隔。UI 在此分隔符上拆分并将每个功能呈现为项目符号。
为什么选择这种格式?
- 易于编辑 - 只需在它们之间输入句号即可
- 无需转义特殊字符
- 在所有 UI 组件中可靠工作
- 易于更新而不破坏 JSON 语法
正确与错误:
选项 2:JSON 数组字符串
"['20GB 高速数据', '无限通话和短信', '包括欧盟漫游']"
UI 也可以解析 JSON 数组。请注意,这是一种包含 JSON 的字符串,而不是数据库中的实际 JSON 数组。
为什么存在这种格式?
- 允许功能中包含句号(例如,“最高 100Mbps。受可用性限制。”)
- 从脚本/API 进行程序生成更容易
- 从使用数组的外部产品目录导入
重要: 这必须是有效的 Python 列表语法作为字符串。每个项目周围使用单引号,整个字符串使用双引号。
使用哪种格式?
- 以句号分隔 - 用于在 UI 中手动创建产品(更简单,错误更少)
- JSON 数组 - 用于基于 API/脚本的产品创建(对于复杂功能更强大)
这两种格式在 UI 中产生相同的输出 - 只是影响您输入数据的方式。
功能在 UI 中出现的位置:
1. 产品目录(客户视图)
当客户浏览可用产品时,功能显示为每个产品卡上的项目符号:
2. 产品详细信息页面
点击“查看详细信息”显示完整的产品信息,包括:
- 产品名称和图标
- 定价(每月费用、设置费用)
- 完整功能列表(项目符号)
- 条款和条件(见下文)
- 可用性和资格
3. 配置确认
在配置过程中,功能显示给用户以供审阅后确认:
功能:• 20GB 高速数据 • 无限通话和短信 • 包括欧盟漫游 • 无合同 • 30 天到期
成本:£15.00/月 设置:£0.00
[取消] [确认并配置]
4. 服务详细信息(配置后)
服务激活后,功能显示在服务详细信息页面供客户参考。
条款和条件格式:
terms 字段是可以包含换行符的纯文本:
条款在 UI 中出现的位置:
1. 产品详细信息页面
条款显示在点击时展开的折叠部分中:
2. 订单确认
在配置过程中,复选框要求用户接受条款:
[配置] 按钮在选中之前被禁用
3. 发票
服务条款可能作为脚注包含在发票中以便清晰。
最佳实践:
- 功能: 保持简洁(每个不超过 50 个字符),关注关键利益
- 条款: 包括关键法律要求、到期政策、公平使用政策
- 两者: 产品变更时更新,以保持客户知情
步骤 8:链接 Ansible 配置剧本
{
"provisioning_play": "play_local_mobile_sim",
"provisioning_json_vars": "{
\"days\": 30,
\"data_gb\": 20,
\"voice_minutes\": \"unlimited\",
\"sms_count\": \"unlimited\"
}"
}
provisioning_play- Ansible 剧本的名称(不带 .yaml 扩展名)provisioning_json_vars- 传递给剧本的默认变量- 剧本必须存在于:
OmniCRM-API/Provisioners/plays/play_local_mobile_sim.yaml
完整产品定义
{
"product_name": "Prepaid Mobile 20GB",
"product_slug": "prepaid-mobile-20gb",
"category": "standalone",
"service_type": "mobile",
"enabled": true,
"icon": "fa-solid fa-sim-card",
"comment": "预付费移动 SIM,包含 20GB 数据,无限通话和短信",
"retail_cost": 15.00,
"wholesale_cost": 5.00,
"retail_setup_cost": 0.00,
"wholesale_setup_cost": 1.00,
"contract_days": 30,
"residential": true,
"business": false,
"customer_can_purchase": true,
"available_from": "2025-01-01T00:00:00Z",
"available_until": null,
"auto_renew": "prompt",
"allow_auto_renew": true,
"inventory_items_list": "['SIM Card', 'Mobile Number']",
"features_list": "[
'20GB 高速数据',
'无限通话和短信',
'包括欧盟漫游',
'无合同',
'30天到期'
]",
"terms": "信用在 30 天后到期。数据、通话和短信仅在到��期间有效。适用公平使用政策。",
"provisioning_play": "play_local_mobile_sim",
"provisioning_json_vars": "{
\"days\": 30,
\"data_gb\": 20,
\"voice_minutes\": \"unlimited\",
\"sms_count\": \"unlimited\"
}"
}
创建附加组件产品
附加组件增强或修改现有服务。它们分为两种类型:虚拟附加组件(没有物理资源)和 硬件附加组件(需要库存)。
示例 1:虚拟附加组件(5GB 数据提升)
一个数字附加组件,向现有移动服务添加数据:
{
"product_name": "5GB Data Boost",
"product_slug": "5gb-data-boost",
"category": "addon",
"service_type": "mobile",
"enabled": true,
"icon": "fa-solid fa-plus",
"comment": "向现有移动服务添加 5GB 额外数据",
"retail_cost": 5.00,
"wholesale_cost": 1.50,
"retail_setup_cost": 0.00,
"wholesale_setup_cost": 0.00,
"contract_days": 0,
"residential": true,
"business": true,
"customer_can_purchase": true,
"auto_renew": "false",
"allow_auto_renew": false,
"inventory_items_list": "[]",
"relies_on_list": "",
"features_list": "5GB 高速数据。有效期 7 天",
"terms": "数据在 7 天后或用��时到期。不可退款。",
"provisioning_play": "play_topup_charge_then_action",
"provisioning_json_vars": "{
\"data_gb\": 5,
\"days\": 7
}"
}
示例 2:硬件附加组件(调制解调器租赁)
一个附加组件,为现有光纤服务提供物理设备:
{
"product_name": "WiFi 6 Modem Rental",
"product_slug": "wifi6-modem-rental",
"category": "addon",
"service_type": "internet",
"enabled": true,
"icon": "fa-solid fa-router",
"comment": "为光纤服务添加 WiFi 6 调制解调器 - 租赁",
"retail_cost": 10.00,
"wholesale_cost": 3.00,
"retail_setup_cost": 0.00,
"wholesale_setup_cost": 45.00,
"contract_days": 30,
"residential": true,
"business": true,
"customer_can_purchase": true,
"auto_renew": "true",
"allow_auto_renew": true,
"inventory_items_list": "['Rental Modem']",
"relies_on_list": "",
"features_list": "WiFi 6 (802.11ax)。双频 2.4GHz + 5GHz。支持最多 40 个设备。家长控制",
"terms": "设备租赁。必须在服务取消时归还,否则将收取 £150 替换费。设备仍为提供商的财产。",
"provisioning_play": "play_addon_assign_modem",
"provisioning_json_vars": "{
\"device_type\": \"modem_router\",
\"requires_configuration\": true
}"
}
附加组件的关键区别:
category: "addon"- 应用于现有服务,而不是独立的contract_days: 0(虚拟)或30(定期租赁) - 计费频率inventory_items_list: "[]"(虚拟)或"['Rental Modem']"(硬件) - 物理资源auto_renew: "false"(一次性)或"true"(租赁) - 定期行为relies_on_list: ""- 为空意味着适用于任何匹配service_type的服务
为什么硬件附加组件需要库存:
硬件附加组件需要 inventory_items_list,因为:
- 跟踪设备 - 知道哪个调制解调器与哪个客户
- 防止缺货 - 如果没有调制解调器,则无法配置附加组件
- 回收 - 当客户取消时,知道需要回收哪些设备
- 成本跟踪 - 将批发成本链接到特定序列号
- 折旧 - 跟踪设备在租赁期间的价值
- 保修 - 通过序列号识别缺陷单位
带库存的附加组件配置流程:
当客户将“WiFi 6 调制解调器租赁”添加到他们的光纤服务时:
- 选择附加组件 - 客户点击“添加到服务”
- 库存选择器出现 - 与独立服务相同:
- 处理付款 - 收取 £10.00 的月租费
- 调制解调器分配 - 更新库存:
service_id: 链接到光纤服务customer_id: 链接到客户item_state: “已分配”
- 触发发货 - 通知履行系统发货调制解调器
- 安装 - 客户收到调制解调器,插入 ONT
- 定期计费 - 每月收取 £10,直到附加组件取消
取消配置硬件附加组件:
当客户取消调制解调器租赁时:
- 取消发起 - 客户点击“移除附加组件”
- 开始退货流程:
- 发送电子邮件,附带退货说明
- 生成预付费运输标签
- 在处罚之前提供 14 天宽限期
- 设备归还:
- 更新库存:
item_state= “在库”(经过翻新后) - 或
item_state= “损坏”(如果有缺陷) - 一旦翻新,链接到下一个客户
- 更新库存:
- 未归还:
- 14 天后,收取 £150 替换费
- 库存标记:
item_state= “丢失” - 收回批发成本(£45)+ 替换价值
附加组件的定价:
附加组件的定价可以与独立服务不同:
- 虚拟附加组件通常没有设置费用
- 硬件附加组件可能有设备的批发设置费用
- 定期租赁附加组件使用
contract_days作为计费频率
阶段 2:配置过程
当客户订��“预付费移动 20GB”产品时,OmniCRM 通过 Ansible 协调配置。
配置流程图
客户订购 → 库存选择 → 创建配置作业 ↓ ↓ 付款授权 ← 变量组装 ← 执行 Ansible 剧本 ↓ ↓ 创建服务记录 → OCS 账户设置 → 库存分配 → 服务激活
步骤配置流程
1. 客户发起订单
从客户页面:
- 员工点击“添加服务”
- 从产品轮播中选择“预付费移动 20GB”
- 显示产品详细信息和定价
2. 库存选择
系统提示所需库存:
- SIM 卡 - 下拉菜单显示可用的 SIM 卡
- 示例:“SIM-00123 - ICCID: 8944...\”
- 手机号码 - 下拉菜单显示可用的电话号码
- 示例:“+44 7700 900123”
员工或客户从可用库存中选择项目。
3. 确认定价
系统显示最终定价:
- 设置费用:£0.00(免费激活)
- 月费用:£15.00
- 今日到期:£15.00(第一个月)
- 续订日期:从今天起 30 天
如果启用自动续订提示,客户选择:
- 每 30 天自动续订此服务
4. 点击配置按钮
当点击“配置”时,API:
- 创建状态为“运行”的
Provision记录(status=1) - 合并来自产品 + 请求 + 库存选择的变量
- 生成后台线程以执行 Ansible 剧本
- 返回 provision_id 到 UI 以进行状态跟踪
5. 变量组装
系统合并来自多个来源的变量:
来自产品:
{
"days": 30,
"data_gb": 20,
"voice_minutes": "unlimited",
"sms_count": "unlimited"
}
来自请求:
{
"product_id": 42,
"customer_id": 123,
"SIM Card": 5001,
"Mobile Number": 5002
}
系统添加:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"initiating_user": 7
}
最终传递给 Ansible 的变量:
{
"product_id": 42,
"customer_id": 123,
"SIM Card": 5001,
"Mobile Number": 5002,
"days": 30,
"data_gb": 20,
"voice_minutes": "unlimited",
"sms_count": "unlimited",
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"initiating_user": 7
}
6. 执行 Ansible 剧本
剧本 play_local_mobile_sim.yaml 使用这些变量执行。
理解 Ansible 配置剧本
让我们检查一个实��的配置剧本,以了解幕后发生的事情。
移动 SIM 配置剧本示例
位置:
OmniCRM-API/Provisioners/plays/play_local_mobile_sim.yaml
高级结构:
- name: 移动 SIM 配置
hosts: localhost
gather_facts: no
become: False
tasks:
- name: 主块
block:
# 1. 加载配置
# 2. 从 API 获取产品详细信息
# 3. 从 API 获取客户详细信息
# 4. 从 API 获取库存详细信息
# 5. 在 OCS 中创建账户(CGRateS)
# 6. 向 OCS 添加余额和津贴
# 7. 在 CRM 中创建服务记录
# 8. 将库存分配给服务
# 9. 记录交易
# 10. 发送欢迎通知
rescue:
# 失败时回滚
# - 删除 OCS 账户
# - 释放库存
# - 记录错误
详细剧本逐步解析:
任务 1:加载配置
- name: 包含 crm_config 的变量
ansible.builtin.include_vars:
file: "../../crm_config.yaml"
name: crm_config
加载系统配置,包括:
- OCS/CGRateS URL 和凭据
- CRM 基本 URL
- 租户配置
任务 2:获取产品详细信息
- name: 从 CRM API 获取产品信息
uri:
url: "{{ crm_config.crm.base_url }}/crm/product/product_id/{{ product_id }}"
method: GET
headers:
Authorization: "Bearer {{ access_token }}"
return_content: yes
register: api_response_product
这做了什么:
- 调用
GET /crm/product/product_id/42 - 检索完整的产品定义
- 存储在
api_response_product变量中
为什么: 即使我们从产品中有 provisioning_json_vars,我们仍然获取完整的产品以获取:
- 最新定价(可能在订单开始后已更改)
- 服务命名所需的产品名称
- 文档的功能列表
- 用于利润跟踪的批发成本
任务 3:设置包事实
- name: 设置包事实
set_fact:
package_name: "{{ api_response_product.json.product_name }}"
monthly_cost: "{{ api_response_product.json.retail_cost }}"
setup_cost: "{{ api_response_product.json.retail_setup_cost }}"
提取常用值以便于可读性。
任务 4:获取库存详细信息
- name: 从 CRM API 获取 SIM 信息
uri:
url: "{{ crm_config.crm.base_url }}/crm/inventory/inventory_id/{{ hostvars[inventory_hostname]['SIM Card'] }}"
method: GET
headers:
Authorization: "Bearer {{ access_token }}"
register: api_response_sim
- name: 从库存响应中设置 IMSI
set_fact:
imsi: "{{ api_response_sim.json.itemtext2 }}"
iccid: "{{ api_response_sim.json.itemtext1 }}"
这做了什么:
- 查找 SIM 卡库存 ID 5001
- 检索 SIM 详细信息:
itemtext1= ICCID(SIM 卡号)itemtext2= IMSI(用户身份)
- 对手机号码库存执行相同操作(检索电话号码)
这有什么重要性:
- IMSI 是在 HSS(家庭用户服务器)中配置用户所需的
- ICCID 在服务备注中记录以便于故障排除
- 电话号码(MSISDN)显示给客户并用于路由
任务 5:生成服务 UUID
- name: 生成 UUID 事实
set_fact:
uuid: "{{ 99999999 | random | to_uuid }}"
- name: 设置服务 UUID
set_fact:
service_uuid: "Local_Mobile_SIM_{{ uuid[0:8] }}"
这做了什么:
- 生成随机 UUID
- 创建类似
Local_Mobile_SIM_a3f2c1d8的 service_uuid
为什么:
- 服务 UUID 是 OCS/CGRateS 中的唯一标识符
- 用于所有计费操作
- 必须在所有服务中全局唯一
任务 6:在 OCS 中创建账户
- name: 在 OCS 中创建账户
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body_format: json
headers:
Content-Type: "application/json"
body:
{
"method": "ApierV2.SetAccount",
"params": [{
"Tenant": "{{ crm_config.ocs.ocsTenant }}",
"Account": "{{ service_uuid }}",
"ActionPlanIds": [],
"ExtraOptions": {
"AllowNegative": false,
"Disabled": false
},
"ReloadScheduler": true
}]
}
register: ocs_create_response
这做了什么:
- 调用 CGRateS JSON-RPC API
- 使用 service_uuid 创建新账户
- 将账户设置为活动状态(未禁用)
- 防止负余额(预付费模式)
为什么:
- OCS 账户是所有计费发生的地方
- 在此存储余额(数据、语音、短信、资金)
- 使用实时跟踪和计费
任务 7:添加数据余额
- name: 添加 20GB 数据余额
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body_format: json
body:
{
"method": "ApierV1.AddBalance",
"params": [{
"Tenant": "{{ crm_config.ocs.ocsTenant }}",
"Account": "{{ service_uuid }}",
"BalanceType": "*data",
"Balance": {
"ID": "DATA_20GB_Monthly",
"Value": 21474836480,
"ExpiryTime": "+720h",
"Weight": 10,
"DestinationIDs": "*any"
}
}]
}
这做了什么:
- 向账户添加 20GB 数据余额
- 值:21474836480 字节(20 * 1024 * 1024 * 1024)
- 在 720 小时(30 天)后到期
- 权重 10(优先消耗较高的权重)
任务 8:添加无限语音和短信
- name: 添加无限语音
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body_format: json
body:
{
"method": "ApierV1.AddBalance",
"params": [{
"Account": "{{ service_uuid }}",
"BalanceType": "*voice",
"Balance": {
"ID": "VOICE_Unlimited",
"Value": 999999999,
"ExpiryTime": "+720h"
}
}]
}
- 添加 999,999,999 秒的语音(基本上是无限的)
- 在 30 天后到期
任务 9:在 CRM 中创建服务记录
- name: 通过 API 添加服务
uri:
url: "{{ crm_config.crm.base_url }}/crm/service/"
method: PUT
body_format: json
headers:
Authorization: "Bearer {{ access_token }}"
body:
{
"customer_id": "{{ customer_id }}",
"product_id": "{{ product_id }}",
"service_name": "Mobile - {{ phone_number }}",
"service_type": "mobile",
"service_uuid": "{{ service_uuid }}",
"service_status": "Active",
"service_provisioned_date": "{{ provision_datetime }}",
"retail_cost": "{{ monthly_cost }}",
"wholesale_cost": "{{ api_response_product.json.wholesale_cost }}",
"icon": "fa-solid fa-sim-card"
}
register: service_creation_response
这创建了什么:
- 与客户关联的服务记录
- 通过
service_uuid链接到 OCS - 存储零售和批发成本
- 将状态设置为“活动”
- 返回
service_id以进行后续操作
任务 10:将库存分配给服务
- name: 将 SIM 卡分配给服务
uri:
url: "{{ crm_config.crm.base_url }}/crm/inventory/inventory_id/{{ hostvars[inventory_hostname]['SIM Card'] }}"
method: PATCH
body_format: json
headers:
Authorization: "Bearer {{ access_token }}"
body:
{
"service_id": "{{ service_creation_response.json.service_id }}",
"customer_id": "{{ customer_id }}",
"item_state": "Assigned"
}
这做了什么:
- 更新 SIM 卡库存记录
- 将
service_id设置为将 SIM 链接到服务 - 将状态从“在库”更改为“已分配”
- 对手机号码库存重复此操作
���什么:
- 跟踪哪个 SIM 分配给哪个客户
- 防止库存的双重分配
- 启用库存报告和审计
任务 11:记录设置成本交易
- name: 添加设置成本交易
uri:
url: "{{ crm_config.crm.base_url }}/crm/transaction/"
method: PUT
body_format: json
headers:
Authorization: "Bearer {{ access_token }}"
body:
{
"customer_id": "{{ customer_id }}",
"service_id": "{{ service_creation_response.json.service_id }}",
"title": "{{ package_name }} - Setup",
"description": "激活费用",
"retail_cost": "{{ setup_cost }}",
"wholesale_cost": "{{ api_response_product.json.wholesale_setup_cost }}"
}
这做了什么:
- 记录 £0.00 的设置费用(零售)到客户
- 记录 £1.00 的批发成本
- 创建交易记录以便开票
任务 12:救援块(错误处理)
rescue:
- name: 失败时删除 OCS 账户
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body:
{
"method": "ApierV2.RemoveAccount",
"params": [{
"Account": "{{ service_uuid }}"
}]
}
- name: 使配置失败
fail:
msg: "配置��败,已回滚 OCS 账户"
这做了什么:
- 如果任何任务失败,救援块执行
- 删除部分创建的 OCS 账户
- 将库存释放回“在库”
- 使配置作业失败并显示错误消息
为什么:
- 防止 OCS 中孤立账户
- 确保在错误时进行干净回滚
- 维护数据一致性
配置完成:创建了什么
成功配置后,系统具有:
1. OCS 账户(CGRateS):
- 账户 ID:
Local_Mobile_SIM_a3f2c1d8 - 余额:
- 20GB 数据(在 30 天后到期)
- 无限语音(999M 秒,在 30 天后到期)
- 无限短信(999M 消息,在 30 天后到期)
2. CRM 服务记录:
- 服务 ID:1234
- 客户:John Doe(customer_id: 123)
- 产品:预付费移动 20GB(product_id: 42)
- 服务名称:“Mobile - +44 7700 900123”
- 服务 UUID:
Local_Mobile_SIM_a3f2c1d8 - 状态:活动
- 月费用:£15.00(零售),£5.00(批发)
- 利润:£10.00/月
3. 库存分配:
- SIM 卡 5001:分配给服务 1234,客户 123
- 手机号码 5002:分配给服务 1234,客户 123
4. 交易记录:
- 创建设置成本交易
- 记录第一个月费用
5. 客户现在可以:
- 在自助服务门户中查看服务
- 查看 20GB 数据余额
- 拨打电话和发送短信
- 充值或添加附加组件
- 实时查看使用情况
阶段 3:添加附加组件和充值
服务激活后,客户可以购买附加组件以增强其服务。
附加组件配置流程
假设客户已使用 20GB 限额中的 18GB,并希望购买“5GB 数据提升”附加组件。
1. 客户导航到服务
- 打开“Mobile - +44 7700 900123”服务页面
- 查看当前使用情况:20GB 中使用 18GB(90%)
- 点击“添加附加组件”或“充值”
2. 系统过滤可用附加组件
仅显示以下附加组件:
category = "addon"service_type = "mobile"(匹配服务类型)residential = true(如果客户是住宅)enabled = true
客户看到:“5GB 数据提升 - £5.00”
3. 客户选择附加组件
- 点击“5GB 数据提升”
- 确认购买 £5.00
- 系统捕获付款授权
4. 附加组件配置启动
系统调用 play_topup_charge_then_action.yaml,并传递变量:
{
"product_id": 43, # 5GB 数据提升产品
"customer_id": 123,
"service_id": 1234, # 现有服务
"access_token": "eyJ...",
"data_gb": 5, # 来自 provisioning_json_vars
"days": 7 # 来自 provisioning_json_vars
}
与独立服务的关键区别:
- 包含
service_id(要修改的现有服务) - 不需要库存
- 不创建服务(修改现有服务)
附加组件配置剧本逐步解析
任务 1:获取服务详细信息
- name: 从 CRM API 获取服务信息
uri:
url: "http://localhost:5000/crm/service/service_id/{{ service_id }}"
method: GET
headers:
Authorization: "Bearer {{ access_token }}"
register: api_response_service
- name: 设置服务事实
set_fact:
service_uuid: "{{ api_response_service.json.service_uuid }}"
customer_id: "{{ api_response_service.json.customer_id }}"
为什么:
- 需要
service_uuid以向正确的 OCS 账户添加余额 - 验证服务是否存在且处于活动状态
- 确保服务属于客户
任务 2:向客户收费
- name: 获取客户的默认支付方式
uri:
url: "http://localhost:5000/crm/stripe/customer_id/{{ customer_id }}"
method: GET
headers:
Authorization: "Bearer {{ access_token }}"
register: api_response_stripe
- name: 获取默认卡 ID
set_fact:
customer_stripe_id: "{{ api_response_stripe.json | json_query(query) }}"
vars:
query: "data[?default_payment_method==`true`].customer_stripe_id | [0]"
- name: 向卡收费
uri:
url: "http://localhost:5000/crm/stripe/charge_card/{{ customer_stripe_id }}"
method: POST
body_format: json
headers:
Authorization: "Bearer {{ access_token }}"
body:
{
"retail_cost": 5.00,
"description": "5GB 数据提升",
"customer_id": "{{ customer_id }}",
"service_id": "{{ service_id }}",
"product_id": "{{ product_id }}",
"wholesale_cost": 1.50,
"invoice": true
}
register: charge_response
- name: 断言付款成功
assert:
that:
- charge_response.status == 200
这做了什么:
- 查找客户的默认 Stripe 付款方式
- 向卡收费 £5.00
- 记录批发成本 £1.50 以便于利润跟踪
- 创建与服务关联的交易
- 添加到下一个发票中
- 如果付款失败,则使配置失败
为什么先收费:
- 在确认付款之前不交付信用
- 防止欺诈
- 将付款与附加组件配置匹配
任务 3:向 OCS 添加数据余额
- name: 添加 5GB 数据余额
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body_format: json
body:
{
"method": "ApierV1.AddBalance",
"params": [{
"Account": "{{ service_uuid }}",
"BalanceType": "*data",
"Balance": {
"ID": "DATA_5GB_Boost_{{ uuid }}",
"Value": 5368709120,
"ExpiryTime": "+168h",
"Weight": 20
}
}]
}
这做了什么:
- 向账户添加 5GB(5368709120 字节)
- 在 168 小时(7 天)后到期
- 权重 20(优先消耗较高的权重 - 提升在每月限额之前消耗)
客户在附加组件后的余额:
- 原始每月:剩余 2GB(在 25 天后到期)
- 新提升:5GB(在 7 天后到期)
- 可用总量:7GB
- 使用顺序:首先消耗提升,然后是每月限额
任务 4:记录交易
- name: 添加附加组件交易
uri:
url: "http://localhost:5000/crm/transaction/"
method: PUT
body_format: json
headers:
Authorization: "Bearer {{ access_token }}"
body:
{
"customer_id": "{{ customer_id }}",
"service_id": "{{ service_id }}",
"title": "5GB 数据提升",
"description": "额外 5GB 数据,有效期 7 天",
"retail_cost": 5.00,
"wholesale_cost": 1.50
}
这做了什么:
- 记录 £5.00 的费用到客户
- 记录 £1.50 的批发成本
- 将交易链接到服务以便于报告
完整附加组件流程摘要
- 客户从过滤列表中选择附加组件
- 付款获得授权并收费
- 数据余额添加到 OCS 账户
- 在 CRM 中记录交易
- 客户立即看到更新的余额:可用 7GB
财务跟踪:
- 服务每月费用:£15 零售,£5 批发
- 附加组件购买:£5 零售,£1.50 批发
自动续订:定期附加组件
某些附加组件可以设置为自动续订(每月数据计划、订阅等)。
产品配置:
{
"product_name": "每月 10GB 数据计划",
"category": "addon",
"retail_cost": 10.00,
"contract_days": 30,
"auto_renew": "true",
"provisioning_play": "play_topup_charge_then_action"
}
配置创建 ActionPlan:
- name: 为自动续订创建 ActionPlan
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body:
{
"method": "ApierV1.SetActionPlan",
"params": [{
"Id": "ServiceID_{{ service_uuid }}__ProductID_{{ product_id }}__MonthlyRenewal",
"ActionPlan": [{
"ActionsId": "Action_{{ product_slug }}",
"Years": "*any",
"Months": "*any",
"MonthDays": "*any",
"WeekDays": "*any",
"Time": "00:00:00",
"Weight": 10
}],
"Overwrite": false,
"ReloadScheduler": true
}]
}
这做了什么:
- 在 OCS 中创建计划任务
- 每 30 天执行
Action_{{ product_slug }} - 动作收费客户并重新应用数据余额
- 直到客户取消为止
客户管理:
- 客户在服务视图中看到“下次续订:2025 年 2 月 1 日 - £10.00”
- 可以点击“取消自动续订”以停止未来收费
- 可以点击“立即续订”以立即应用下个月的配额
阶段 4:取消配置服务
当客户取消服务时,系统必须干净地移除所有资源。
取消配置触发器
取消配置可以由以下原因触发:
- 客户取消 - 客户点击“取消服务”
- 行政操作 - 员工标记服务以进行停用
- 未付款 - 服务因未续订而到期
- 合同结束 - 固定期限合同达到结束日期
取消配置流程
1. 客户发起取消
- 导航到服务
- 点击“取消服务”
- 系统提示:“您确定吗?任何剩余余额将被没收。”
- 客户确认
2. 宽限期(可选)
某些运营商实施宽限期:
- 服务标记为“待取消”
- 在 7-30 天内保持活动状态
- 客户可以在宽限期内撤销取消
- 宽限期后自动取消配置
3. 创建取消配置作业
系统创建配置作业,内容如下:
{
"action": "deprovision",
"service_id": 1234,
"customer_id": 123,
"service_uuid": "Local_Mobile_SIM_a3f2c1d8"
}
调用在 service.deprovisioning_play 中指定的剧本或原始剧本的救援块。
4. Ansible 取消配置剧本
- name: 取消配置移动服务
hosts: localhost
tasks:
- name: 禁用 OCS 账户
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body:
{
"method": "ApierV2.SetAccount",
"params": [{
"Account": "{{ service_uuid }}",
"ExtraOptions": { "Disabled": true }
}]
}
- name: 删除 ActionPlans(停止自动续订)
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body:
{
"method": "ApierV1.RemoveActionPlan",
"params": [{
"Id": "ServiceID_{{ service_uuid }}__*"
}]
}
- name: 更新 CRM 中的服务状态
uri:
url: "http://localhost:5000/crm/service/{{ service_id }}"
method: PATCH
body:
{
"service_status": "Deactivated",
"service_deactivate_date": "{{ current_datetime }}"
}
- name: 释放库存到库存
uri:
url: "http://localhost:5000/crm/inventory/inventory_id/{{ sim_card_id }}"
method: PATCH
body:
{
"service_id": null,
"customer_id": null,
"item_state": "Decommissioned"
}
这做了什么:
- 禁用 OCS 账户 - 停止所有收费,阻止使用
- 删除 ActionPlans - 取消自动续订
- 更新 CRM 服务 - 状态“已停用”,记录日期
- 释放库存 - SIM 标记为“退役”,可供重用(翻新后)
5. 取消配置后
系统执行清理:
- 客户不再在自助服务门户中看到服务
- 服务仍保留在 CRM 中以便于历史报告
- 交易和发票保留以便于会计
- 库存可以翻新并重用
- OCS 账户可以在保留期后归档
部分与完全取消配置
部分取消配置(暂停):
- 用于未付款或临时暂停
- OCS 账户被禁用但未删除
- 保留余额
- 收到付款时可以重新启用
- name: 暂停服务
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body:
{
"method": "ApierV2.SetAccount",
"params": [{
"Account": "{{ service_uuid }}",
"ExtraOptions": { "Disabled": true }
}]
}
完全取消配置(永久取消):
- 用于永久取消
- OCS 账户完全删除
- 余额没收
- 无法重新启用
- name: 删除 OCS 账户
uri:
url: "http://{{ crm_config.ocs.cgrates }}/jsonrpc"
method: POST
body:
{
"method": "ApierV2.RemoveAccount",
"params": [{
"Account": "{{ service_uuid }}"
}]
}
产品管理的最佳实践
产品生命周期管理
产品状态:
enabled: true- 产品可供新订单enabled: false- 产品禁用,现有服务继续
禁用产品:
- 将产品标记为
enabled: false以防止新订单 - 现有服务保持活动状态
- 客户仍然可以续订/修改现有服务
- 对于逐步淘汰旧产品非常有用
库存管理
库存状态:
New- 新库存,准备分配In Stock- 可供配置Assigned- 与客户服务关联Decommissioned- 可以翻新并重用Damaged- 需要修理或处置
重用库存:
取消配置后:
- SIM 卡:翻新并标记为“在库”
- 电话号码:在端口期后释放(30 天)
- 设备:测试、翻新、标记为“已使用”
配置指标
监控:
- 配置成功率
- 平均配置时间
- 常见故障点
- 库存周转率