ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

汽车供应链数据底座:ERP/SRM取数到python-pptx自动出PPT

汽车供应链数据底座:ERP/SRM取数到python-pptx自动出PPT 简介这是一份面向汽车行业管理者、供应链从业者及咨询顾问的PPT文档资料主题为汽车行业供应链管理最佳实践。内容围绕行业变革下的供应链议题展开涵盖供应链战略定义、组织结构与全球化协同、供应商与主机厂整合带来的风险应对、关键绩效指标设计与绩效衡量、信息技术在需求预测与物流追踪中的应用以及最佳实践与基准比较、客户与第三方协作等模块适合用于内部培训、课题研究或管理信息化方案参考。资源包内含1个pptx文件压缩包约851KB属典型的咨询汇报型演示文稿页数精炼、图表与目录结构清晰可直接引用其框架与观点。目前已有84人学习下载。读者可从中获取完整的供应链诊断思路与改进路径包括Push到Pull三阶段演进脉络、供应链管理者的角色定位、成本与能力预测方法以及绩效测量和使能技术选型的落地建议。1. 评审会前一夜最贵的不是顾问费率而是库存口径汽车行业的供应链评审最后几乎都会收敛成一个.pptx十几页到几十页前面是行业判断中间是断供风险、呆滞库存、齐套率、Tier-N 穿透后面是三年降本路线图。真正吃掉时间的从来不是排版而是「同一个库存周转天数财务算出来是 58 天计划算出来是 41 天」——口径差异没人拍板页面上每一个数字都要重来一遍。这份标题背后讲的是一件事如何把 ERP、SRM、WMS、MES 里的原始数据变成一份口径自洽、可以每周重生成、经得起客户供应链总监逐页追问的咨询交付物。适合三类人做供应链数字化和 SOP 的数据同学、要给客户出诊断报告的实施顾问、以及被临时拉去「帮着做几页 PPT」的 IT 支持。判断标准很朴素——客户拿走这份文件后能不能用同一套逻辑在内部复现出相近的数。2. 汽车供应链 PPT 的数据底座从 ERP/SRM 取数到统一指标口径2.1 六个核心指标的口径分歧与判定规则咨询级页面上的每个数字都必须先回答「谁定义的、从哪张表来、时间窗口是什么」。汽车供应链里最容易吵起来的六个指标口径分歧点几乎固定指标通用公式主要取数表常见分歧客户 OTD 准时交付率按承诺日期足量交付行数 / 总交付行数销售订单、发运单按行还是按台承诺日期取原始承诺还是改期后供应商 OTD按计划到货日到货行数 / 总订单行数SRM 采购订单、收货单是否含让步接收±1 天是否算准时库存周转天数 DSI期末库存金额 / 期间日均出库成本库存快照、成本表用金额还是数量分母用 90 天还是滚动 12 个月齐套率可齐套工单数 / 计划开工工单数MES 工单、BOM、库存是否含替代料是否按关键件单独统计缺料停线时长因缺料导致的停线分钟数累计MES 停机记录换型停机是否剔除待料与质量停线如何切分呆滞库存 EO 占比超期库存金额 / 总库存金额库存快照、出入库流水超期阈值 90/180/365 天是否扣减已有订单覆盖口径一旦定下来就要写进 PPT 的备注页或者附录页而不是藏在 Excel 的批注里。常见的做法是拉一张metric_definition维表把指标编码、公式文本、owner、生效日期都存进去报告生成时直接读出来渲染到附录——这样下一版评审时口径变化有据可查而不是靠谁的记忆。2.2 用一条 SQL 拉出可复用的供应链明细宽表不要把十几个指标拆成十几个脚本分别跑后期对不上数就是灾难。做法是先建一张以「物料 × 工厂 × 周」为粒度的宽表所有指标从这张表派生-- 供应链周度宽表粒度 物料 * 工厂 * 自然周 WITH inv AS ( -- 库存快照取每周日 23:59 的期末库存 SELECT material_id, plant_id, week_id, SUM(qty) AS inv_qty, SUM(qty * std_cost) AS inv_amount FROM f_inventory_snapshot WHERE snapshot_time week_end_time -- 只用周末快照避免重复计入 GROUP BY material_id, plant_id, week_id ), cons AS ( -- 消耗按工单实际投料倒推周消耗 SELECT material_id, plant_id, week_id, SUM(issue_qty) AS cons_qty, SUM(issue_qty * std_cost) AS cons_amount FROM f_material_issue GROUP BY material_id, plant_id, week_id ), supply AS ( -- 供应采购到货 自制入库 SELECT material_id, plant_id, week_id, SUM(CASE WHEN src_type PO THEN recv_qty ELSE 0 END) AS po_recv_qty, SUM(CASE WHEN src_type PROD THEN recv_qty ELSE 0 END) AS prod_recv_qty, SUM(CASE WHEN recv_date plan_date THEN recv_qty ELSE 0 END) AS late_recv_qty FROM f_receipt GROUP BY material_id, plant_id, week_id ) SELECT i.material_id, i.plant_id, i.week_id, m.material_type, -- 关键件/长周期件标记用于齐套口径 i.inv_qty, i.inv_amount, COALESCE(c.cons_qty, 0) AS cons_qty, -- 周转天数期末库存 / 期间日均消耗分母为 0 时置 NULL 而非 0 CASE WHEN COALESCE(c.cons_qty, 0) 0 THEN NULL ELSE i.inv_qty * 7.0 / c.cons_qty END AS dsi_days, s.po_recv_qty, s.late_recv_qty, CASE WHEN COALESCE(s.po_recv_qty s.prod_recv_qty, 0) 0 THEN NULL ELSE s.late_recv_qty * 1.0 / (s.po_recv_qty s.prod_recv_qty) END AS supplier_late_rate FROM inv i LEFT JOIN cons c ON c.material_id i.material_id AND c.plant_id i.plant_id AND c.week_id i.week_id LEFT JOIN supply s ON s.material_id i.material_id AND s.plant_id i.plant_id AND s.week_id i.week_id LEFT JOIN dim_material m ON m.material_id i.material_id;几个参数值得注意。snapshot_time week_end_time这条过滤条件是把库存快照压成周粒度的关键如果不加同一物料在一周内的多张快照会被累加DSI 直接翻几倍这是新手最常踩的坑。dsi_days里乘 7 是因为粒度是周若改成月粒度就必须乘 30分母为 0 时返回NULL而不是 0是为了让下游在画图时能区分「没有消耗」和「周转极快」否则那些停产料的 DSI 会变成 0 天把整体均值拉得好看得不真实。2.3 BOM 递归展开与 Tier-N 供应商穿透断供风险那一页要追问到二级、三级供应商就必须做 BOM 递归展开。以 PostgreSQL 为例-- 从成品出发递归展开到 N 层并关联各层物料的供应商 WITH RECURSIVE bom_tree AS ( SELECT b.parent_id, b.child_id, b.qty_per, -- 单位用量 1 AS level, b.qty_per AS cum_qty -- 累计用量逐层相乘 FROM dim_bom b WHERE b.parent_id :finished_good_id -- 入口要分析的成品编码 AND b.effective_to IS NULL -- 只取当前生效版本 UNION ALL SELECT b.parent_id, b.child_id, b.qty_per, t.level 1, t.cum_qty * b.qty_per FROM dim_bom b JOIN bom_tree t ON b.parent_id t.child_id WHERE t.level :max_level -- 参数最大展开层数一般 5~8 AND b.effective_to IS NULL ) SELECT t.level, t.child_id, SUM(t.cum_qty) AS total_qty_per_unit, s.supplier_name, s.country_code, s.single_source_flag -- 独家供货标记 FROM bom_tree t LEFT JOIN dim_supplier_material s ON s.material_id t.child_id GROUP BY t.level, t.child_id, s.supplier_name, s.country_code, s.single_source_flag ORDER BY t.level, total_qty_per_unit DESC;:max_level这个参数不要设太大。BOM 里如果存在循环引用工程变更遗留问题递归会一直跑下去直到超时实务上除了限制层数还应该在dim_bom里加一条自检WHERE parent_id child_id。cum_qty逐层相乘的含义是「生产一台成品需要多少该层物料」这是后面算暴露量的基础。single_source_flag是穿透分析的重点——同一层里既有独家供货、又位于长周期品类才值得在 PPT 上单独占一页把所有物料都列出来等于没有重点。3. 用 python-pptx 把指标装配成咨询级页面3.1 模板先行母版与版式命名约定别用代码去画每一个文本框那是在用程序员的方式做设计师的活。正确顺序是让顾问把模板做好把颜色、字体、页脚、页码全部固化在母版里代码只负责往占位符里灌内容。版式名用途占位符约定LYT_COVER封面title、subtitle、dateLYT_KPI_3三指标概览页kpi1_val/kpi1_label…kpi3_labelLYT_CHART_HALF左图右文chart_ph、bodyLYT_TABLE_FULL全宽数据表table_phLYT_APPENDIX口径附录body版式名一旦定下就不要再改代码里用名字索引而不是索引号这样顾问在模板里调整版式顺序时不会把脚本跑歪。3.2 往占位符灌数python-pptx 的核心 APIfrom pptx import Presentation from pptx.util import Cm, Pt import pandas as pd TPL auto_supply_chain_template.pptx prs Presentation(TPL) def layout_by_name(prs, name): 按版式名取版式避免索引号变动导致错版 for lay in prs.slide_layouts: if lay.name name: return lay raise KeyError(f模板中找不到版式: {name}) def set_ph(slide, name, text, sizeNone): 向指定占位符写文本占位符不存在时静默跳过便于模板演进 for ph in slide.placeholders: if ph.name name: ph.text str(text) if size: for p in ph.text_frame.paragraphs: for r in p.runs: r.font.size Pt(size) return True return False # ---- 1) 封面 ---- s prs.slides.add_slide(layout_by_name(prs, LYT_COVER)) set_ph(s, title, 某汽车零部件集团 供应链诊断) set_ph(s, subtitle, 断供风险 · 库存健康度 · 齐套交付) set_ph(s, date, pd.Timestamp.today().strftime(%Y-%m)) # ---- 2) KPI 概览页 ---- kpi pd.read_sql(SELECT * FROM v_kpi_weekly WHERE week_id :w, conn, params{w: week}) kpi kpi.set_index(kpi_code) s prs.slides.add_slide(layout_by_name(prs, LYT_KPI_3)) pairs [(OTD, kpi.loc[customer_otd, value], 客户准时交付率), (DSI, kpi.loc[inv_days, value], 库存周转天数), (EO, kpi.loc[eo_ratio, value], 呆滞库存占比)] for i, (code, val, label) in enumerate(pairs, start1): # 百分比类指标放大 100 倍展示天数类保留一位小数 disp f{val * 100:.1f}% if code in (OTD, EO) else f{val:.1f} set_ph(s, fkpi{i}_val, disp, size40) set_ph(s, fkpi{i}_label, label, size14) prs.save(output/supply_chain_review.pptx)layout_by_name用名字匹配是刻意的——模板改版时版式顺序经常变用prs.slide_layouts[3]这种写法迟早出事。set_ph里占位符找不到时返回False而不抛异常是为了让模板还在迭代的阶段脚本不至于直接崩掉但上线后建议改成抛错否则漏灌一个 KPI 格子在评审现场才发现。百分比放大 100 倍那段逻辑要写死在渲染层不要让上游 SQL 提前乘好否则同一份数据在表页和图页会出现 100 倍差异。3.3 原生图表还是贴图按刷新频率选这一步的取舍直接影响后期维护成本方案优点代价适用场景pptx 原生图表客户可在 PPT 里直接改数据、换配色样式控制弱复杂图做不出来趋势线、柱状对比这类标准图matplotlib 出 PNG样式完全可控能做瀑布图、热力图数据变化必须重跑脚本结构化的瀑布图、甘特、供应链地图Excel 图表链接顾问自己能改跨机器路径易断链演示型、不要求每周刷新实务上我一般按「每周刷新」这条线切要每周重生成的图全走 matplotlib出 PNG 后用slide.shapes.add_picture(png, Cm(x), Cm(y), widthCm(w))贴进chart_ph占位符只有那两三张客户当场想改数字的对比图才用原生图表。贴图时统一按占位符宽度等比缩放不要在代码里硬编码高宽否则模板一改比例所有图都会变形。4. 断供、呆滞、齐套三类高频议题的分析模型怎么落到参数上4.1 Tier-N 断供风险打分权重与暴露量风险打分页最容易变成拍脑袋常见的可靠做法是把风险拆成三个可计算的分量供货集中度、地域或物流暴露、交付历史表现。维度计算方式建议权重参数标定说明单一来源度该料供应商数 1 记 1.0 2 记 0.5≥3 记 00.40独家供货是硬风险交付表现近 12 周迟到率归一化到 [0,1]0.35用滚动窗口而非全历史库存覆盖当前库存 / 周均消耗反比映射0.25覆盖 2 周直接置 1import numpy as np, pandas as pd def risk_score(df, w(0.40, 0.35, 0.25)): # 1) 单一来源度0/0.5/1 三档 df[s_single] df[supplier_cnt].map(lambda n: 1.0 if n 1 else (0.5 if n 2 else 0.0)) # 2) 交付表现迟到率 0~30% 线性映射到 0~1超过 30% 封顶 df[s_delivery] (df[late_rate_12w] / 0.30).clip(0, 1) # 3) 库存覆盖覆盖周数 2 周记 1 8 周记 0中间线性 df[s_cover] (1 - (df[cover_weeks] - 2) / 6).clip(0, 1) # 4) 加权求和再乘以 BOM 暴露量单台用量 × 月产量做优先级排序 df[risk] df[s_single] * w[0] df[s_delivery] * w[1] df[s_cover] * w[2] df[exposure] df[cum_qty] * df[monthly_volume] return df.sort_values([risk, exposure], ascendingFalse)权重不用追求精确关键是三点一是三档映射而不是连续打分避免小数位上的争论二是迟到率用 12 周滚动窗口太久的历史会把已经改善的供应商一直钉在风险榜上三是最后必须乘exposure否则榜单头部全是那些用量极小、风险再高也砸不出水花的料号。cover_weeks的分母用周均消耗而不是月均汽车行业排产波动以周为单位用月度会明显钝化。4.2 EO 呆滞库存与安全库存服务水平 z 值怎么取呆滞库存页要能回答两个问题现有多少金额可能计提以及为了不断供至少要压多少安全库存。两者加起来才是净风险敞口。import numpy as np, pandas as pd from scipy.stats import norm # 服务水平 - z 值不要手抄直接算 def z_of(service_level: float) - float: return norm.ppf(service_level) # 95% - 1.645, 99% - 2.326 def safety_stock(avg_w, std_w, lead_time_w, z): return z * np.sqrt(lead_time_w) * std_w # 需求独立假设下的经典公式 eo pd.read_sql( SELECT material_id, inv_amount, DATEDIFF(day, last_issue_date, CURRENT_DATE) AS no_move_days, future_demand_amt -- 未来 12 个月已有订单/预测覆盖金额 FROM v_inventory_age , conn) EO_DAYS 180 # 呆滞阈值半年无动 eo[is_eo] eo[no_move_days] EO_DAYS eo[net_eo] (eo[inv_amount] - eo[future_demand_amt].fillna(0)).clip(lower0) eo[is_eo_net] eo[is_eo] (eo[net_eo] 0) print(eo.groupby(is_eo_net)[net_eo].agg([count, sum]))阈值EO_DAYS一定要和客户财务确认因为账面计提规则常常是从最后一次出库日开始算 180 天而不是从入库日。net_eo里减去未来需求覆盖金额这一步不能省否则在新项目爬坡阶段为量产准备的长周期件会被误判成呆滞页面上写着几千万风险客户看一眼就知道数据没洗干净。服务水平取值上汽车行业关键件通常 99%非关键件 95%两者的 z 值差 0.68落到金额上往往是几百万的差别这个参数必须写在页脚注释里不能只给结果。4.3 齐套率与 JIS 排产按周滚动的缺口模拟齐套率的价值在于提前几周看出哪个工单会缺件。做法是按周滚动对每个计划开工工单做需求与可用量的对撞import pandas as pd def kitting_check(orders, onhand, inbound, weeks8, key_onlyTrue): orders : 工单需求明细字段 order_id, material_id, week_id, req_qty, is_key onhand : 当前可用库存 material_id, avail_qty inbound: 未来到货计划 material_id, week_id, in_qty已按供应商 OTD 打折 rows [] for w in range(1, weeks 1): wk_in inbound[inbound.week_id w].groupby(material_id)[in_qty].sum() o orders[orders.week_id w] if key_only: o o[o.is_key] need o.groupby(material_id)[req_qty].sum() shortage (need - onhand.set_index(material_id)[avail_qty] - wk_in).clip(lower0) rows.append({ week: w, order_cnt: o.order_id.nunique(), short_sku: int((shortage 0).sum()), kitting_rate: 1 - (shortage 0).sum() / max(len(need), 1), }) # 本周占用的库存要扣减否则后续周会重复计算可用量 consumed need.subtract(shortage, fill_value0) onhand[avail_qty] onhand[avail_qty] - onhand[material_id].map( consumed).fillna(0) onhand[avail_qty] onhand[avail_qty].clip(lower0) return pd.DataFrame(rows)inbound里的到货量要按供应商历史 OTD 打折比如某供应商履约率 85%就按in_qty * 0.85计入否则齐套率会被高估客户照着这个表排产第二周就停线。key_onlyTrue表示只统计关键件这是汽车行业的惯例——一颗螺丝没到不叫缺料一颗芯片没到才叫。最后那段库存扣减是最容易写错的地方如果不扣第 2 周开始每周都会看到同样的库存齐套率曲线会平得反常一眼就能看出模型有问题。5. 一键重生成与口径自检让这份 pptx 下周还能用页面的价值在于可复现否则它只是一次性的手工活。收尾要落到两件事数字自检和一键重跑。5.1 跨页数字一致性自检最常见的翻车是 KPI 页写 OTD 92.4%、趋势图上是 91.8%因为两处取的时间窗口差了几天。做法是渲染前跑一遍断言把关键数字落到一个manifest里图和表都从它读checks [ (customer_otd, kpi_val, chart_vals[-1], 1e-4), # 容差 0.01 个百分点 (inv_days, kpi_val, table_val, 1e-6), ] for name, a, b, tol in checks: if abs(a - b) tol: raise AssertionError(f{name} 跨页不一致: 概览{a} 明细{b})容差要对齐指标的显示精度百分比页面上保留一位小数容差就设 0.0005即 0.05 个百分点以内天数保留一位小数容差 0.05 天。设得太松自检等于没做设成 0 又会因为浮点误差天天误报。5.2 把取数、算指标、出图和写页面串成一条命令用一个入口脚本按顺序调四步每步产出落盘到带日期的目录出错时能准确定位到是哪一段断了#!/usr/bin/env bash set -euo pipefail RUN$(date %Y%m%d_%H%M) OUTruns/${RUN}; mkdir -p ${OUT} python etl/extract_supply_chain.py --week ${WEEK_ID} --out ${OUT}/fact.parquet python etl/build_metrics.py --fact ${OUT}/fact.parquet --out ${OUT}/kpi.parquet python charts/render_all.py --kpi ${OUT}/kpi.parquet --out ${OUT}/png python deck/build_deck.py --kpi ${OUT}/kpi.parquet --png ${OUT}/png \ --template templates/auto_supply_chain_template.pptx \ --out ${OUT}/供应链诊断报告.pptxset -euo pipefail保证任何一步失败立刻停不会拿半份数据去生成一份看起来正常的报告——这比事后再去核对页面上的数字要省事得多。--week参数是唯一需要人工指定的输入其余全部由数据推导这样每周只需要改一个参数。跑完在runs/下留一份完整快照客户问「上个月那版是多少」时直接翻对应目录就能复现不用重跑也不用翻聊天记录。最后一层技巧是把manifest.json连同 pptx 一起归档里面存下当次的口径版本号和关键参数EO_DAYS、服务水平、权重下次评审如果数字变了是数据变了还是口径变了一比对就清楚。本文还有配套的精品资源点击获取
返回列表