简介:这份 Oracle ERP 供应链解决方案 PPT 面向企业信息化顾问、供应链计划人员与 ERP 实施学习者,梳理 Oracle EBS 在供应链领域的整体思路,可用于方案宣讲与项目选型参考。内容围绕从需求到交付的端到端流程,串起销售、物流、计划、制造、采购与财务环节,并按战略层、计划层、运营层三层组织,涵盖需求计划、S&OP、供应计划、物料计划、生产排程、交期承诺、仓储运输等模块。
较具参考价值的是预测与计划方法部分:贝叶斯混合模型预测引擎、十五种预测模型、促销效果与基线需求拆解、多组织集中计划平台、分时段供应网络配置,以及基于约束和基于成本的优化计划、订单承诺模拟、供应商协同、供需双向追溯与例外信息管理等,均配流程示意图,便于对照理解业务逻辑。
资源为单个 pptx 文件,压缩包约 2.3MB,打开即可浏览完整章节,已有 120 人学习,适合用作供应链解决方案的入门概览与方案素材。
1. 一份 Oracle ERP 供应链解决方案.pptx,评审时被问倒的从来不是排版
评审会上最难堪的一幕,从来不是 PPT 做得难看,而是供应链负责人翻到第七页,指着库存周转率那一栏问:这个数从哪张表取的?同样的数,上一页和这一页为什么差了 0.3 个百分点?我见过不少团队把 Oracle ERP 供应链解决方案.pptx 做成一本漂亮的流程图册,模块名一个不落,一碰数字就含糊。
这份 deck 真正要装的三样东西是:一条能用 erp系统业务流程 串起来的价值链、一组能追溯到 Oracle 数据库真实表的指标口径、一张把安全库存和提前期写明白的参数页。前者决定业务方信不信你懂行,后面两样决定项目启动后你会不会被追着改数。
这篇写给三类人:正在做 Oracle ERP 供应链售前方案的实施顾问、需要把供应链数字化讲清楚的产品负责人、第一次被要求「做一份 SCM 方案 PPT」的工程师。默认你大概知道订单、采购、库存是什么,默认你手上有只读账号,也默认你机器上能装 Python 和 python-pptx。
2. 先划边界:供应链方案该写进 PPT 的 6 个业务域
供应链方案最怕写成「ERP 全模块介绍」,页数铺到 60 页,业务方记住的却只有流程图。我的习惯是先划边界:只写跟物料流动和订单履行强相关的六个域,其他一律砍掉或者塞进附录。这六个域是主数据与组织、需求与计划、采购与寻源、库存与仓储、订单与发运、成本与绩效。划完之后你会发现,很多本来想讲的财务、人力内容其实不必出现,页数也从六十页压到二十页上下。
划边界的另一个好处是让 PPT 的每一页都有数据出口。业务域一旦确定,它对应的 Oracle 表、关键字段、指标口径也就确定了,写页的时候顺手把「数据出处」记在备注栏,评审时被追问能立刻答上来,这是最省事的自证方式。
2.1 从「订单到收款、采购到付款」拆出六个必写域
订单到收款这条线,走的是 OE_ORDER_HEADERS_ALL 到 WSH_NEW_DELIVERIES 的发运链路,讲的是承诺日期、发运日期、开票日期三者怎么对齐。采购到付款这条线,走的是 PO_HEADERS_ALL 到 RCV_TRANSACTIONS 的收货链路,讲的是采购提前期、收货差异、三单匹配。两条线中间夹着库存与计划,物料从在途变成在库,从在库变成在制,再变成可发运的成品。
把这六域画成一张横向的价值链图放在第 3 页,后面每一页都标注自己在这张图上的位置,业务方的注意力会稳得多。我一般会在这一页下面加一行小字:本次方案覆盖的域是这六个,WMS 的自动化立库改造、运输管理 TMS 对接不在范围内。把边界写死,比后面被追问「这个你没写是不是不做」要轻松。
演示环境这一栏也要提前定好。PPT 里的截图和数据到底来自测试库还是生产只读备库,配置上要写清楚。Oracle 19c 单实例搭 ADG 做只读备库是最常见的做法,报表和取数全部走备库,生产主库不接任何查询,这一条往往也是「erp库存场景高并发的解决方案」里最便宜、见效最快的一招。PPT 上可以只写一句「取数走只读节点」,但你自己心里要清楚主备切换、延迟监控、连接串怎么写。
2.2 六个域对应的主表和关键字段,先建一张映射表
写页之前,我会先建一张映射表贴在草稿里。没有这张表,写到第三页你就会开始怀疑自己写的字段名到底对不对,或者在 PPT 里写出「库存表」这种没法验证的描述。
| 业务域 | 典型页 | 主表 | 关键字段 | 常见坑 |
|---|---|---|---|---|
| 主数据与组织 | 物料与供应商编码规则 | MTL_SYSTEM_ITEMS_B、AP_SUPPLIERS | SEGMENT1、ORGANIZATION_ID、INVENTORY_ITEM_ID | 物料编码在组织间不唯一,PPT 写死一个编码容易对不上 |
| 需求与计划 | 预测与 MRP 逻辑 | MSC_SYSTEM_ITEMS、MRP_FORECAST_DATES | PLAN_ID、FORECAST_DESIGNATOR | 计划名写错,现场演示跑不出数 |
| 采购与寻源 | 采购到付款流程 | PO_HEADERS_ALL、PO_LINES_ALL、RCV_TRANSACTIONS | PO_HEADER_ID、LINE_NUM、QUANTITY_RECEIVED | 把「收货」和「入库」混成一件事 |
| 库存与仓储 | 库存事务、子库货位 | MTL_ONHAND_QUANTITIES_DETAIL、MTL_MATERIAL_TRANSACTIONS | TRANSACTION_QUANTITY、SUBINVENTORY_CODE、LOCATOR_ID | 在手量含预留,净可用量对不上 |
| 订单与发运 | 订单到收款 | OE_ORDER_HEADERS_ALL、WSH_NEW_DELIVERIES | REQUEST_DATE、ACTUAL_DEPARTURE_DATE | 实际发运日期为空时没做兜底 |
| 成本与绩效 | KPI 看板 | CST_ITEM_COSTS、自定义 KPI 结果表 | ITEM_COST、PERIOD_KEY | 月结前后成本口径不同,同页两个数打架 |
这张表还有一个隐藏用途:它就是你数据出处附录的雏形。表格里的每一行,最后都会变成附录页的一行「指标名—库—schema—表—刷新频率」。评审现场有人问口径,你翻到附录就能答,比现场翻代码体面得多。
提示:如果你做的是 Fusion Cloud 而不是 EBS,表名和字段名差别很大,别把 EBS 的 MTL_ 系列表名直接搬进云版的方案页,这是资深顾问一眼就能看出来的破绽。
2.3 二十页的骨架怎么排,谁讲哪一段
页数定下来,接下来的问题是节奏。二十页左右是我用着最顺的篇幅:能讲完六个域,又不会让听众在第 25 页开始看手机。下面这套骨架我在几个项目里反复调过,可以直接抄。
| 页码 | 内容 | 主讲人 | 需要的数据 |
|---|---|---|---|
| 1–2 | 封面、议程 | 售前 | 无 |
| 3 | 供应链价值链总览图,标注本次范围 | 售前 | 无 |
| 4 | 现状痛点(按六个域分别列一到两条) | 业务顾问 | 现场调研记录 |
| 5–6 | 目标业务流程,订单到收款 | 业务顾问 | 现状订单数据 |
| 7–8 | 目标业务流程,采购到付款 | 业务顾问 | 采购历史数据 |
| 9 | 计划与需求协同逻辑 | 计划顾问 | 预测准确率 |
| 10–11 | 库存与仓储管理,含子库货位策略 | 业务顾问 | 在手量与呆滞清单 |
| 12 | KPI 看板与口径定义 | 数据同学 | 近 12 个月指标快照 |
| 13–14 | 参数配置页(安全库存、提前期、MOQ) | 计划顾问 | 参数基线表 |
| 15 | 库存高并发场景的取数架构 | 数据同学 | 备库/物化视图说明 |
| 16–17 | 集成与接口清单 | 集成同学 | 接口对照表 |
| 18 | 实施计划与里程碑 | 项目经理 | 排期 |
| 19 | 风险与假设 | 项目经理 | 无 |
| 20 | 附录:数据出处与表映射 | 数据同学 | 全部指标清单 |
这张表里真正容易做砸的是 12 到 15 页。前面讲流程的时候大家都点头,一进入指标和参数,业务方就会拿计算器跟你对账。所以这三四页的内容必须在开会前跑过一遍真数据,而不是拍脑袋填。
3. 让数字站得住:从 Oracle 库里捞出供应链 KPI 的几段 SQL
PPT 里真正会被追问的只有两类数字:一类是现状有多痛,一类是上线后能改善多少。这两类数字都来自同一批 SQL。我的做法是不在 PPT 工具里算数,全部在库里算完,把结果落成一张快照表,PPT 只负责展示。这样做的最大好处是可复现——三个月后有人质疑数字,你重跑一遍存储过程就行。
下面几段 SQL 都是可以直接改参数用的,覆盖了呆滞判定、OTIF 口径固化、分页取数和高并发下的取数策略。要注意的是,查询尽量走只读备库或专用只读账号,别拿应用账号往生产主库上怼,这是最基本的职业习惯。
3.1 呆滞库存与在手量:一条 SQL 跑出压了 90 天的料
呆滞清单是供应链方案里最有说服力的一页,因为它直接指向钱。判定逻辑说起来简单:在手量大于零,且最近一次库存事务距今超过 N 天。难点在性能,MTL_MATERIAL_TRANSACTIONS 在大多数生产库里都是亿级表,不加时间范围直接扫,会把 DBA 招来。
-- 呆滞物料清单::org_id 库存组织,:idle_days 呆滞判定天数 WITH onhand AS ( SELECT organization_id, inventory_item_id, SUM(transaction_quantity) AS onhand_qty FROM mtl_onhand_quantities_detail WHERE organization_id = :org_id GROUP BY organization_id, inventory_item_id ), last_txn AS ( -- 只扫最近 24 个月的事务,避免全表扫描亿级事务表 SELECT organization_id, inventory_item_id, MAX(TRUNC(transaction_date)) AS last_txn_date FROM mtl_material_transactions WHERE organization_id = :org_id AND transaction_date >= ADD_MONTHS(TRUNC(SYSDATE), -24) GROUP BY organization_id, inventory_item_id ) SELECT msi.segment1 AS item_code, msi.description, o.onhand_qty, t.last_txn_date, TRUNC(SYSDATE) - NVL(t.last_txn_date, TRUNC(SYSDATE)) AS idle_days FROM onhand o JOIN mtl_system_items_b msi ON msi.inventory_item_id = o.inventory_item_id AND msi.organization_id = o.organization_id LEFT JOIN last_txn t ON t.organization_id = o.organization_id AND t.inventory_item_id = o.inventory_item_id WHERE o.onhand_qty > 0 AND TRUNC(SYSDATE) - NVL(t.last_txn_date, TRUNC(SYSDATE)) >= :idle_days ORDER BY idle_days DESC, o.onhand_qty DESC FETCH FIRST 200 ROWS ONLY;几个参数和写法上的讲究。:org_id是库存组织 ID,多组织场景下要循环传入或者改成 IN 列表;:idle_days就是 PPT 上要写的判定天数,通常取 90 或 180,两个口径都跑一遍,页面上给两行数。TRUNC(SYSDATE)是整个呆滞口径的锚点,一定要把两边都截断到日,否则同一份数据早上跑和下午跑出来的天数不一样,PPT 上写着 91 天,第二天变成 90 天,业务方会怀疑你的严谨性。
LEFT JOIN那段是给「24 个月内一次事务都没有」的物料兜底的。这类料在业务上最恶劣,但因为不在 last_txn 结果集里,如果写成 INNER JOIN 就会被整条过滤掉,页面上呆滞金额凭空少一块。用 NVL 把它按「从未动过」处理,idle_days 按从今天算起,虽然数字偏保守,但不会漏。
还有一个坑是「在手量含预留」。MTL_ONHAND_QUANTITIES_DETAIL 里的 transaction_quantity 是实物在手,不扣已被订单或工单预留的部分。如果你的 PPT 上写了「可用库存」四个字,就得再关联预留表把预留量减掉,或者干脆把页面标题改成「实物在手量」,少一个歧义少一轮扯皮。
3.2 订单及时交付率:用一段 PL/SQL 把口径固化成结果表
OTIF 这类指标最忌讳在 PPT 里写一个「约 87%」。要用,就得写清楚分母是什么、分子是什么、时间窗口是哪一段。我的做法是写一个存储过程,按月生成快照,PPT 只读快照表。下面这段是简化版,表关系按标准链路做了省略,实际项目里请以 DBA 给的 ER 图为准。
CREATE OR REPLACE PROCEDURE xx_scm_otif_snap(p_period IN VARCHAR2) IS v_start DATE; v_end DATE; BEGIN -- p_period 形如 '202506',起止日期按自然月切 v_start := TO_DATE(p_period || '01', 'YYYYMMDD'); v_end := LAST_DAY(v_start); -- 重跑幂等:先删同一期间再插,避免重复执行翻倍 DELETE FROM xx_scm_kpi_snap WHERE kpi_code = 'OTIF' AND period_key = p_period; INSERT INTO xx_scm_kpi_snap (period_key, kpi_code, org_id, nume, deno, ratio, load_time) SELECT p_period, 'OTIF', t.org_id, t.nume, t.deno, ROUND(t.nume / NULLIF(t.deno, 0) * 100, 2), SYSDATE FROM ( SELECT oh.org_id, -- 分子:实际发运不晚于订单承诺日期 SUM(CASE WHEN TRUNC(NVL(wd.actual_departure_date, wd.expected_departure_date)) <= TRUNC(oh.request_date) THEN 1 ELSE 0 END) AS nume, -- 分母:期间内所有已发运订单头 COUNT(DISTINCT oh.header_id) AS deno FROM oe_order_headers_all oh JOIN oe_order_lines_all ol ON ol.header_id = oh.header_id JOIN wsh_delivery_details wdd ON wdd.source_header_id = ol.header_id AND wdd.source_line_id = ol.line_id JOIN wsh_delivery_assignments wda ON wda.delivery_detail_id = wdd.delivery_detail_id JOIN wsh_new_deliveries wd ON wd.delivery_id = wda.delivery_id WHERE oh.order_date BETWEEN v_start AND v_end AND oh.org_id = :org_id GROUP BY oh.org_id ) t; COMMIT; END xx_scm_otif_snap; /参数上只有两个要交代:p_period决定期间,:org_id决定组织,其余都是口径。真正需要跟业务方签字确认的是三件事:第一,分母只算已发运的订单,还是包含逾期未发运的订单;第二,实际发运日期为空时用期望发运日期兜底是否可接受;第三,同一天发运算不算及时。这三条不定死,同一个人今天算 87%、明天算 84%,方案的可信度就没了。
NULLIF(t.deno, 0)是防除零的,看起来是小事,但当一个新组织当月没有任何发运时,没有它整段过程会直接抛错中断。执行频率我一般挂在 DBMS_SCHEDULER 里,每月 1 号凌晨跑上月,同时提供手工执行入口,方便评审前临时补数。这段过程本身也是「oracle存储过程」在供应链取数场景里最典型的用法:把口径写进代码,而不是写进某个人的 Excel。
顺带说两个取数时几乎一定会用到的小技巧。一个是字符串里混了非数字字符导致转换报错,用REGEXP_LIKE(col, '^[0-9]+(\.[0-9]+)?$')先过滤,或者 12c 之后直接用TO_NUMBER(col DEFAULT NULL ON CONVERSION ERROR);另一个是接口表里常见的时间戳,如果存的是毫秒数,转日期就是TO_DATE('19700101','YYYYMMDD') + col / 86400000,展示时再用TO_CHAR(..., 'YYYY-MM-DD HH24:MI:SS.FF3')保留毫秒。这两招在写数据校验页的时候能省掉半天排查。
3.3 库存高并发下的取数:只读节点、物化视图和分页
供应链方案的 KPI 页要好看,背后就得频繁跑重查询。生产主库上跑这些查询,轻则报表变慢,重则锁等待堆积、应用超时,这时候 DBA 会直接找上门。我踩过一次:一个没做时间范围限制的呆滞查询,赶在月末结账时段跑,把库存事务表的缓冲区全冲掉了,之后所有进销存操作都变慢,那次是血泪经验。
常见的做法是三层削峰。第一层是读写分离,19c 单实例搭 ADG,报表和取数全部走只读备库,主库不接查询。备库的连接串里把 service_name 写清楚,别用 SID 硬连,主备切换后 SID 可能变。第二层是物化视图,把按天聚合的结果预先算好,PPT 取数只读物化视图。
-- 按天的库存快照物化视图,PPT 取数只读它 CREATE MATERIALIZED VIEW mv_scm_onhand_daily BUILD IMMEDIATE REFRESH COMPLETE ON DEMAND AS SELECT TRUNC(SYSDATE) AS snap_date, organization_id, inventory_item_id, SUM(transaction_quantity) AS onhand_qty FROM mtl_onhand_quantities_detail GROUP BY TRUNC(SYSDATE), organization_id, inventory_item_id; -- 每天 01:00 刷新,避开月末结账和白天业务高峰 BEGIN DBMS_SCHEDULER.CREATE_JOB( job_name => 'JOB_MV_SCM_ONHAND_DAILY', job_type => 'PLSQL_BLOCK', job_action => 'BEGIN DBMS_MVIEW.REFRESH(''MV_SCM_ONHAND_DAILY'', ''C''); END;', start_date => TRUNC(SYSDATE) + 1 + 1/24, repeat_interval => 'FREQ=DAILY; BYHOUR=1; BYMINUTE=0', enabled => TRUE); END; /第三层是分页和采样。明细页一次只取 50 到 200 行,12c 之后直接用OFFSET ... FETCH NEXT ... ROWS ONLY,老版本就用 ROWNUM 三层嵌套。分页一定要带稳定排序键,只按 idle_days 排序的话,同一批数据两次查询顺序可能不同,翻页会漏行重行。
-- 12c 及以上分页,排序键要唯一化,避免翻页抖动 SELECT item_code, onhand_qty, idle_days FROM mv_scm_onhand_delay ORDER BY idle_days DESC, item_code ASC OFFSET :offset ROWS FETCH NEXT :page_size ROWS ONLY;如果只是想看一眼数据分布、不想出正式数字,可以在表名后加SAMPLE(1)做 1% 采样,秒级返回。但采样结果绝对不能写进 PPT 的正式指标页,它只适合你自己判断数据长什么样。真要看性能瓶颈,去 v$session 里按 sid、serial#、event 捞一把,看清楚是 IO 等待还是并发等待,比在 PPT 上写「性能已优化」有说服力一百倍。
注意:所有取数脚本里别硬编码账号密码。走只读账号加环境变量,PPT 生成脚本从环境变量读连接串。生产账号共享给一堆人用,出了事根本查不到是谁跑的。
4. 用 python-pptx 批量出页:封面、参数页和附录页怎么生成
方案页的数据一旦从库里出来,剩下的就是往 PPT 里塞。手工复制粘贴最大的问题是不可复现——数据改了要重贴二十页,贴错一行还不一定发现。用 python-pptx 把「数据查询」和「页面生成」串成一条脚本,改数只要重跑一次,这是我最推荐的落地方式。
但别一上来就想全自动生成。我的建议是先手工做一份母版,把版式、字体、配色、页脚都定好,脚本只负责往占位符里填内容。全自动生成出来的 PPT 往往丑得没法看,改版式反而比手工做更费时间。
4.1 页面对象模型:母版、版式和占位符怎么用
先理解 python-pptx 的层级:Presentation 里有 slide_layouts,每个 layout 来自母版 slide_master,版式上又挂着若干 placeholder。脚本该干的事是按索引取版式、按占位符索引填文本,而不是用绝对坐标画一堆文本框。用绝对坐标的后果是换台机器、换个字号,整页错位。
from pptx import Presentation from pptx.util import Cm, Pt from pptx.dml.color import RGBColor # 母版里约定:版式 0 封面、版式 1 章节页、版式 5 表格页 prs = Presentation("tpl_scm_solution.pptx") def add_cover(prs, title, subtitle): slide = prs.slides.add_slide(prs.slide_layouts[0]) slide.placeholders[0].text = title slide.placeholders[1].text = subtitle return slide def add_table_page(prs, title, headers, rows, col_widths=None): slide = prs.slides.add_slide(prs.slide_layouts[5]) slide.placeholders[0].text = title shape = slide.shapes.add_table( len(rows) + 1, len(headers), Cm(1.5), Cm(3.2), Cm(23), Cm(0.8 * (len(rows) + 1))) table = shape.table for i, h in enumerate(headers): cell = table.cell(0, i) cell.text = h # 表头加粗并压到 12pt,正文 10pt,避免行高被撑爆 for p in cell.text_frame.paragraphs: for r in p.runs: r.font.size = Pt(12) r.font.bold = True r.font.color.rgb = RGBColor(0x1F, 0x3B, 0x63) for r_i, row in enumerate(rows, start=1): for c_i, val in enumerate(row): cell = table.cell(r_i, c_i) cell.text = str(val) for p in cell.text_frame.paragraphs: for r in p.runs: r.font.size = Pt(10) return slide add_cover(prs, "Oracle ERP 供应链解决方案", "覆盖计划、采购、库存、订单与成本六大域") prs.save("out_scm_solution.pptx")这里有几个参数值得单独说。版式索引 0、1、5 是我在母版里自己约定的,你在自己的模板里要先数一遍,别照抄索引,否则填出来的页会奇形怪状。add_table的宽高单位是 EMU,用 Cm 包一层更直观,行高至少留 0.8 厘米,字号超过 12pt 表格就会溢出页面。表格列宽不显式设置的话,python-pptx 会均分,指标名很长的列会被挤成两行,显得很业余。
另一个细节是占位符索引。母版改版后索引可能偏移,稳妥的做法是按 placeholder_format.idx 或者按 name 找,而不是按位置序号。我一般会在脚本开头加一段断言,找不到约定的占位符就直接报错退出,宁可跑失败也不要生成一份缺标题的半成品。
4.2 参数页怎么写:安全库存、提前期、MOQ 三列定生死
方案 PPT 里最容易被反复翻的一页是参数页。业务方不一定关心你的架构图画得多漂亮,但一定会盯着安全库存和提前期问「这个数怎么来的」。参数页的信息密度要高,我的习惯是一行一个物料大类,列固定为:物料类别、计划员、安全库存、采购提前期、处理提前期、MOQ、固定批量、批量倍数、目标服务水平。
| 物料类别 | 计划员 | 安全库存 | 采购提前期(天) | 处理提前期(天) | MOQ | 固定批量 | 批量倍数 | 目标服务水平 |
|---|---|---|---|---|---|---|---|---|
| 关键电子料 | 张工 | 按 14 天用量 | 45 | 2 | 500 | 1000 | 500 | 98% |
| 结构件 | 李工 | 按 7 天用量 | 30 | 1 | 200 | 500 | 100 | 95% |
| 包装辅料 | 王工 | 按 3 天用量 | 10 | 0.5 | 5000 | 10000 | 5000 | 90% |
| 备品备件 | 王工 | 按 30 天用量 | 60 | 1 | 10 | 50 | 10 | 99% |
这张表要配一段生成代码,把参数基线直接从库里读出来填进去,避免手工抄错。
import os import oracledb from pptx import Presentation # 连接串走环境变量,脚本里不出现账号密码 dsn = oracledb.makedsn(os.environ["ORA_HOST"], int(os.environ["ORA_PORT"]), service_name=os.environ["ORA_SERVICE"]) conn = oracledb.connect(user=os.environ["ORA_USER"], password=os.environ["ORA_PWD"], dsn=dsn) SQL_PLAN_PARAM = """ SELECT category_name, planner_name, safety_stock_desc, lead_time_days, processing_days, moq, fixed_lot, lot_multiple, service_level FROM xx_scm_plan_param_v WHERE plan_id = :plan_id ORDER BY category_name """ with conn.cursor() as cur: cur.execute(SQL_PLAN_PARAM, plan_id=1001) rows = cur.fetchall() prs = Presentation("tpl_scm_solution.pptx") headers = ["物料类别", "计划员", "安全库存", "采购提前期(天)", "处理提前期(天)", "MOQ", "固定批量", "批量倍数", "目标服务水平"] add_table_page(prs, "计划参数基线(示例组织)", headers, rows) prs.save("out_scm_solution.pptx")这段代码里真正需要交代的是口径而不是写法。采购提前期指的是从下单到收货入库的自然日,不含周末顺延规则;处理提前期是收货到可用的检验和上架时间;MOQ 和批量倍数常常一起用,MOQ 决定最小起订,批量倍数决定下单量必须能被它整除。这三个参数在系统里配错,跑出来的计划建议就会碎成一堆奇怪的数量,业务方一看就会说「系统算得不对」。
安全库存那一列我习惯写「按 X 天用量」而不是直接写数量。直接写数量的话,需求一波动这页就过期了。写成天数描述,再在备注里附上计算公式和当前基线值,既稳定又能落到系统参数。
4.3 图表和 KPI 页自动生成,顺带做个自检函数
KPI 页不要放截图。截图最大的问题是数据更新后必须重新截,而且缩放到投影仪上经常糊。用 python-pptx 加 matplotlib 生成图片再插进页面,重跑脚本就自动更新,省心得多。做法是先用 matplotlib 把图画成 PNG 存到临时目录,再slide.shapes.add_picture插进去,图片宽度按页面宽度留 2 厘米边距。
更值得花时间的是自检函数。同一场评审里,图文不一致是最丢分的错误。我一般写一个函数,把图表里用的数值和表格里填的数值做一次比对,差超过 0.1 就抛异常。
def assert_consistent(table_rows, chart_values, key_index=0, val_index=4, tol=0.1): """表格里的值必须和图表数据源一致,避免出现两页数字打架""" table_map = {row[key_index]: float(row[val_index]) for row in table_rows} for name, val in chart_values.items(): assert name in table_map, f"图表中的 {name} 不在表格里" diff = abs(table_map[name] - float(val)) if diff > tol: raise ValueError(f"{name} 表格值 {table_map[name]} 与图表值 {val} 相差 {diff:.2f}") return True这个函数跑通一次,能挡掉后面所有「同一指标两张页数不一致」的尴尬。参数上tol给 0.1 是经验值,百分比类指标允许一位小数误差,绝对金额类指标就该把 tol 压到 0。key_index和val_index按你表格的实际列位置改,别硬编码在函数内部。
5. 避坑:Oracle ERP 供应链 PPT 翻车最多的 5 个场景
这一章是纯经验,每一条都是我在评审现场或者项目启动后被追着改数时踩出来的。写方案的人一般不太愿意承认这些,但提前知道,能省掉好几次返工。
现象一:同一指标在两页上数字不一样。第 12 页写库存周转率 5.8 次,第 15 页的柱状图显示 6.1 次。原因几乎总是口径或者时间窗口不同,一页用的是本月,一页用的是近三个月,作图的人又没注意。解决办法是把所有指标统一从快照表取数,图表和表格共用同一个查询结果对象,再用上面那个自检函数跑一遍。凡是手工在 PPT 里改过数字的地方,都要在备注里标出来源。
现象二:表名字段名张冠李戴。最典型的是把 EBS 的 MTL_SYSTEM_ITEMS_B 写进云版方案的架构页,或者把采购订单头表写成 PO_HEADERS 而漏了 _ALL 后缀。这类错误业务方看不出来,但评标的技术专家一眼就能看穿。解决办法是每个表名都从数据字典里复制,不要凭记忆敲。写完之后用一段 SQL 批量校验表名是否存在,存在性检查能挡掉大部分低级错误。
现象三:现场演示连不上库。演示前一晚还好好的,第二天一早连不上,多半是监听或者连接配置的问题。常见原因有三个:连接串里用了 SID 而不是 service_name,主备切换后 SID 变了;plsql 或者 dbeaver 的连接配置里 TNS 别名指向的地址过期;服务端的监听端口被改过但客户端还是老配置。解决办法是演示前做一次完整的连库演练,并且准备一台已经装好客户端、配置好 TNS 的备用机器。真到了现场连不上,直接切截图页,别在台上折腾十分钟。
现象四:把性能方案写成「上了 RAC 就不卡了」。这句话在有经验的 DBA 面前等于没说。库存高并发场景的取数优化,真正能写进方案的是具体动作:报表走只读备库、重查询改物化视图、明细页分页、大事务错峰调度、慢 SQL 按等待事件分类治理。页面上最好有一两个具体数字,比如「报表查询从主库剥离后,结账时段平均响应从 4.2 秒降到 0.6 秒」,这比写十句架构口号都有用。
现象五:参数页写死具体数值,需求一波动就过期。安全库存直接写 1200 件,采购提前期直接写 45 天,三个月后业务量翻倍,这页就没人敢用了。改成写规则加当前基线,例如「安全库存 = 日均用量 × 14 天,当前基线 1200 件」,同时把参数存到系统里由计划模块维护。方案页要表达的是规则和治理方式,而不是某一天的具体数字。
注意:不要在 PPT 里展示生产环境截图里的真实账号、IP、实例名和库名。内部评审无所谓,跨部门或者对外场合,这类信息该打码就打码。
6. 交付前最后 30 分钟:把这份 .pptx 变成能复现的活方案
方案稿交出去的那一刻,它就开始过期了。真正能让这份 Oracle ERP 供应链解决方案.pptx 活下来的,是你有没有在附录里留下足够多的复现线索。我现在的习惯是固定加一页「数据出处清单」,四列:指标名、库和 schema、来源表、刷新频率。比如库存周转率写「只读备库/SCM_RO、MV_SCM_ONHAND_DAILY 与 CST_ITEM_COSTS、每日 01:00」。这一页平时没人看,一旦有人质疑数字,它就是你的后悔药。
现场演示我只做三个保命动作。第一,所有 KPI 提前跑成快照并导出图片,断网也能讲;第二,演示用的库账号用只读账号,权限范围写进备注,万一被问起能立刻说清;第三,把最关键的三个查询单独存成一个可直接执行的脚本文件,现场要加指标就当场跑,比嘴上解释十句管用。这三个动作加起来不超过二十分钟准备时间,但能挡掉大多数意外。
如果要把这套东西沉淀成团队资产,我的做法是把「取数脚本 + PPT 生成脚本 + 参数基线表」放在同一个目录下,用一份配置文件管库连接和期间参数。下次做新项目,改配置、重跑脚本,二十页方案初稿一小时内出来,剩下时间全部用来跟业务方对口径。这才是自动化真正省下来的时间,不是省在排版上。
最后说一个我自己的教训。前几年做一份库存优化方案,参数页的安全库存我是手工从旧方案里复制过来的,当时觉得反正业务方也不会细看。结果启动会上,计划员当场把两个物料的参数拿出来跟现有系统比对,全对不上,那次之后的两个月,方案里每一个数字都被反复抽查。从那以后我给自己定了个死规矩:任何写进方案的数字,必须在脚本里能跑出来,跑不出来的就写「待确认」,绝不留估数。这个习惯一开始让初稿出得慢一些,但它省下的返工时间,比慢出来的那两天多得多。
希望帮到你。
本文还有配套的精品资源,点击获取