资讯动态

汽车供应链管理PPT自动化:BOM/OTD口径与python-pptx生成

发布时间:2026/9/17 21:16:21 来源:尧图企业网站定制
简介这份面向汽车行业管理者与供应链从业者的PPT资料围绕供应链管理在汽车产业中的核心地位展开适合企业战略规划、组织架构调整及供应链数字化转型等场景的学习与内部培训参考。压缩包内仅含1个pptx文件体积约851KB以幻灯片形式系统梳理了行业变革动因、组织与全球供应链协同、关键绩效指标设定、信息技术应用、最佳实践与基准比较以及客户、供应商与第三方协作角色等模块并配有目录导航与阶段演进脉络便于按主题快速定位。目前已有84人学习下载。读者可借助其中的供应链成熟度自评框架、成本与风险管理思路以及绩效衡量方法用于梳理自身供应链流程、识别瓶颈并制定改进方向尤其适合希望从战略层面理解汽车供应链运作、推动跨部门协同的中高级管理人员参考。1. 从一份汽车供应链管理咨询 PPT 说起它其实是个数据工程问题「某咨询汽车行业供应链管理.pptx」这个文件名本身就透露了很多信息它不是一份一次写完就归档的报告而是一份会被反复刷新、反复交付的模板化产物。里面通常是几十页——产能爬坡、订单交付率、库存周转、单一供应商风险、物流成本拆解。反直觉的地方在于这类 PPT 的返工大半不是排版问题而是口径对不上同一页里销量用的是批发口径交付率用的是终端零售口径翻到第三页就被客户当场问住。把它当一个数据工程问题来做更划算——先把 BOM、MRP、OTD 的口径钉死再用 Python 算指标最后让脚本把数字灌进母版。这套做法适合每周或每月要交一版供应链分析、又不想手工贴图对数字的人。2. 汽车供应链数据的口径与取数BOM、MRP、OTD 三类主数据的对齐2.1 汽车供应链的四层物料层级与 BOM 展开汽车供应链的层级比大多数制造业深一层。整车厂OEM往下是 Tier1 总成供应商再往下 Tier2 零件、Tier3 原材料一份「整车交付延迟」的结论往往要沿这条链往下追三层才能定位到真实原因。做分析前先把维度表理清比急着写 SQL 更重要。层级典型主键常见来源表容易踩的坑整车/车型model_codedim_model年款与车型混用跨年款聚合失真系统/模块module_iddim_module一个模块跨多车型映射表缺行总成assembly_pnbom_header没过滤 BOM 生效日期零件part_nobom_item替代料没做等价合并BOM 展开最容易出事的点是生效日期。汽车行业的工程变更ECN频繁同一颗零件上半年和下半年的用量可能不同如果不按订单交付日期去选版本算出来的物料需求会整体偏移。-- 按交付日期选择生效中的 BOM 版本避免用到未来版本或已废止版本 WITH bom_eff AS ( SELECT parent_pn, part_no, qty_per, scrap_rate FROM bom_item WHERE plant_code :plant -- 工厂维度必须带多工厂共线时不能省 AND :deliver_date eff_from AND :deliver_date COALESCE(eff_to, DATE 9999-12-31) ) SELECT b.parent_pn, b.part_no, SUM(o.qty * b.qty_per * (1 b.scrap_rate)) AS gross_require_qty FROM fact_order o JOIN bom_eff b ON b.parent_pn o.assembly_pn WHERE o.order_date BETWEEN :d0 AND :d1 GROUP BY 1, 2;参数说明:plant用来隔离工厂:deliver_date是选 BOM 版本的基准一般取承诺交付日而不是下单日scrap_rate是报废率计划口径和财务口径对报废的处理方式不同做需求测算时要显式带上否则缺口会被系统性低估。提示替代料alternate part如果不做等价合并同一个物理约束会被拆成两行库存看板会显示「两边都不够、两边都有余」。2.2 用 SQL 把 OTD 口径算出来OTDOn-Time Delivery是这类 PPT 里出现频率最高的指标也是争议最多的。争议来源通常不是公式而是「On-Time」对标的是承诺日promised date还是原计划日original plan date。汽车行业客户计划频繁滚动承诺日会被改原计划日不会。建议两个都算页面上并排展示。SELECT o.order_id, o.model_code, o.promised_date, a.actual_date, DATEDIFF(day, o.promised_date, a.actual_date) AS delay_days, CASE WHEN a.actual_date o.promised_date THEN 1 ELSE 0 END AS otd_flag FROM fact_order o LEFT JOIN fact_delivery a ON a.order_id o.order_id AND a.deliver_type FIRST_SHIP -- 只取首次发运退货/返修不算 WHERE o.order_status CANCELLED AND o.promised_date BETWEEN :d0 AND :d1;:d0和:d1建议按承诺日切窗口而不是按下单日这样每个月的 OTD 是「本月应交付的订单里按时交付了多少」跟客户对账时口径一致。deliver_type这个过滤条件别省否则补发、换货会把延迟天数算成负数OTD 会虚高。2.3 取数时最容易错的三处第一是时间基准混用。下单日、承诺日、实际发运日、客户签收日四个日期至少对应四套说法交付分析里必须写明用的是哪一套。第二是退货与返修。汽车行业的三包、召回、返修单如果混进正常发运交付量会虚增且延迟天数全为负。第三是替代料与共用件。共用件被多个总成引用时做缺口测算要用「总需求 - 可用库存」而不是逐个总成单独算否则会重复扣减库存。注意宽表落地时把口径字段一起写进去比如otd_basis PROMISED、window_type PROMISED_MONTH半年后别人接手才不会重新猜一遍。2.4 落成宽表的字段约定中间层建议直接落成一张按「零件 × 供应商 × 月份」聚合的宽表PPT 里所有图表都从它出这样数字对不上时只需要查一个地方。字段含义用途part_no零件号主键之一supplier_code / supplier_tier供应商与层级风险页分组month_key月份时间轴otd_rate该月 OTD交付页delay_p90延迟天数 P90交付页尾部风险inv_qty / avg_daily_usage库存与日均用量周转页dos库存天数周转页risk_score缺料风险分风险页3. 用 Python 算汽车供应链核心指标OTD、库存周转、缺料风险的关键参数3.1 交付履约OTD 与延迟天数的分位数平均数在交付分析里基本没用。一个供应商 OTD 85%可能是 85% 的订单都准时也可能是 60% 准时、另外 40% 全是超期一个月的大单被平均掉了。用分位数看尾部。import pandas as pd df pd.read_parquet(dws_sc_delivery.parquet) # 只保留首次发运且延迟天数为正负各留负数代表提前 df df[df[deliver_type] FIRST_SHIP] otd (df.groupby([supplier_code, month_key]) .agg( orders(order_id, nunique), otd_rate(otd_flag, mean), delay_p50(delay_days, lambda s: s.quantile(0.50)), delay_p90(delay_days, lambda s: s.quantile(0.90)), delay_max(delay_days, max), ) .reset_index()) # 订单量太少的组合不参与排名避免 3 单全准时排到第一 otd otd[otd[orders] 20] print(otd.sort_values(delay_p90, ascendingFalse).head(10))orders 20这个门槛是经验值样本太小时 OTD 的置信区间宽到没法用。delay_p90用来驱动「尾部风险」那一页比平均值更有说服力客户真正难受的是那 10% 的超长延迟不是整体均值。3.2 库存周转与 DOS分母口径决定你被质疑的次数库存周转率 出库成本 / 平均库存成本。分子分母的期间必须对齐且平均库存要用期初期末平均而不是期末数。汽车行业月内波动大直接用期末库存会把周转算得忽高忽低。# 日均用量取近 3 个完整月排除当月未结束导致的低估 COMPLETE_MONTHS 3 usage (df_usage[df_usage[is_complete_month]] .groupby(part_no)[usage_qty] .sum() / (COMPLETE_MONTHS * 30)) inv df_inv.groupby(part_no)[inv_qty].mean() dos (inv / usage).rename(dos).reset_index() # 用量为 0 的呆滞件单独分桶不要让它把周转率拉到无穷大 slow_moving dos[dos[dos].isna() | (dos[dos] 180)]is_complete_month这个标记位建议在数据层就打好别在分析层用「当前月」去猜脚本在下个月 1 号跑出来的结果会完全不同。DOS 超过 180 天的零件单独出一页呆滞清单这在汽车行业往往比周转率本身更受关注。3.3 缺料风险打分把波动、集中度和提前期放进一个可解释模型缺料风险页最忌讳的是给出一个没有解释力的「风险指数」。建议用三个可解释因子线性加权权重写在配置里客户质疑时能当场改。import numpy as np def minmax(s): return (s - s.min()) / (s.max() - s.min() 1e-9) W {cv: 0.4, concentration: 0.35, lt: 0.25} # 权重显式写死 feat base.copy() feat[cv] minmax(feat[demand_std] / (feat[demand_mean] 1e-9)) # 需求波动 feat[concentration] minmax(1 / feat[qualified_supplier_cnt]) # 供应商集中度 feat[lt] minmax(feat[lead_time_days]) # 采购提前期 feat[risk_score] (W[cv] * feat[cv] W[concentration] * feat[concentration] W[lt] * feat[lt]) * 100 # 有安全库存覆盖的降权避免把所有长周期件都标红 feat[risk_score_adj] feat[risk_score] * np.where(feat[dos] 45, 0.6, 1.0)qualified_supplier_cnt只统计通过认证的合格供应商把「名义上有三家、实际只有一家能供」的情况算准。dos 45的降权系数 0.6 是经验值含义是库存天数超过 45 天的零件即使波动大短期也不会断线。3.4 必须显式写进配置的阈值参数建议初值影响最小订单样本量20低于此值不参与 OTD 排名完整月窗口3日均用量的计算区间DOS 呆滞阈值180 天呆滞清单的切分线安全库存覆盖天数45 天风险分降权触发线风险分权重0.4 / 0.35 / 0.25波动、集中度、提前期# sc_config.yml —— 每次交付只改这一份脚本和 PPT 都读它 otd: min_orders: 20 window: promised_month inventory: complete_months: 3 slow_moving_days: 180 risk: min_dos_for_discount: 45 discount: 0.6 weights: {cv: 0.4, concentration: 0.35, lt: 0.25}把阈值集中在一份 YAML 里好处是版本对比时只看一个文件也能顺手写进 PPT 的脚注页。4. 生成汽车行业供应链管理.pptxpython-pptx 的母版、版式与图表落地4.1 为什么不要从零画形状用add_textbox一个个摆位置前二十页能跑通到客户改一次版式就全崩。正确做法是让设计师先做一份母版把页眉、页码、结论条、图表区都做成版式slide layout里的占位符placeholder脚本只负责往占位符里灌内容。这样客户改字体、换配色脚本一行不用动。版式名用途占位符约定封面报告首页idx 0 标题idx 1 副标题单图表结论条交付、周转页idx 0 标题idx 1 正文图表锚点在第 3 个两栏对比供应商对比idx 0 标题idx 1 / idx 2 两栏正文表格页参数表、清单idx 0 标题idx 1 表格占位附录口径说明idx 0 标题idx 1 正文4.2 读取版式并批量灌入内容from pptx import Presentation from pptx.util import Pt, Cm TPL template/汽车行业供应链管理_母版.pptx prs Presentation(TPL) LAYOUT_CHART 1 # 按母版里的实际顺序调不要硬编码名字 def add_page(prs, title, bullets, layout_idxLAYOUT_CHART): slide prs.slides.add_slide(prs.slide_layouts[layout_idx]) # 占位符索引在不同母版里不固定按 idx 取比按 name 稳 ph {p.placeholder_format.idx: p for p in slide.placeholders} ph[0].text title tf ph[1].text_frame tf.clear() # 不清空会残留母版里的示例文字 for i, line in enumerate(bullets): para tf.paragraphs[0] if i 0 else tf.add_paragraph() para.text line para.level 0 para.font.size Pt(14) return slide add_page(prs, Q3 交付履约复盘, [整体 OTD 84.2%, 尾部延迟 P90 为 11 天]) prs.save(out/汽车行业供应链管理.pptx)tf.clear()是最容易被忘的一行不清空时母版里的示例文字会跟着一起输出翻页看到「此处填写结论」很尴尬。Pt(14)直接显式设字号不依赖母版默认值否则不同机器上渲染出来的行高会变长文本容易溢出文本框。4.3 图表原生图表和贴图各司其职PPT 里的图分两类。需要客户当场改数据、改口径的用 pptx 原生图表纯展示的排名条形图、甘特式趋势图用 matplotlib 出高 DPI 图再插入样式可控性更高。from pptx.chart.data import CategoryChartData from pptx.enum.chart import XL_CHART_TYPE from pptx.util import Cm data CategoryChartData() data.categories top10[supplier_code].tolist() data.add_series(延迟天数 P90, tuple(top10[delay_p90])) gf slide.shapes.add_chart( XL_CHART_TYPE.BAR_CLUSTERED, Cm(1.5), Cm(4.0), Cm(22.0), Cm(9.0), data ) chart gf.chart chart.has_legend False # 单系列不需要图例省地方 chart.value_axis.has_major_gridlines FalseBAR_CLUSTERED在供应商排名场景比COLUMN_CLUSTERED好读因为供应商编码一般是长字符串横向排布不用旋转标签。图表锚点用Cm而不是Inches中文字体下Cm换算更接近设计师给的稿。4.4 几个必踩的坑中文字体如果母版用的是系统中文字体而生成环境比如容器里没装脚本跑出来会静默回退成默认字体字宽变化导致换行位置全变。生成镜像里把字体文件装上。图片 DPImatplotlib 默认 100 DPI投到会议室大屏上会糊。保存时显式设dpi200并控制单页图片体积整套 PPT 控制在 30MB 以内方便邮件发送。文件句柄Presentation对象在保存前一直持有文件在同一进程里连续生成多版本时先save再重新加载母版不要复用同一个对象改来改去容易出现版式错乱。提示生成脚本的输出目录固定为out/并把文件名写成固定值别带时间戳。客户每次拿到的文件名一致版本管理交给 git而不是靠文件名区分。5. 让 .pptx 每次刷新都可信一致性校验与版本对比的实用技巧5.1 生成后自动跑的三项校验一份自动生成的报告最容易在三个地方翻车占位符没填、数字和上页对不上、页码乱。这三项都能在保存后自动查。from pptx import Presentation def audit(path, expect_pagesNone): prs Presentation(path) problems [] for i, slide in enumerate(prs.slides, 1): for ph in slide.placeholders: txt ph.text_frame.text.strip() if ph.has_text_frame else if not txt: problems.append(f第 {i} 页占位符 {ph.placeholder_format.idx} 为空) if 点击此处 in txt or 示例 in txt: problems.append(f第 {i} 页残留母版提示文字: {txt[:20]}) if expect_pages and len(prs.slides) ! expect_pages: problems.append(f页数不符: 实际 {len(prs.slides)}预期 {expect_pages}) return problems for p in audit(out/汽车行业供应链管理.pptx, expect_pages42): print(p)has_text_frame的判断不能省图表占位符没有 text_frame直接取.text会抛异常。expect_pages作为参数传入而不是写死章节增删时手动改一次比事后翻页找问题快。5.2 用反读文本做口径回归比校验空占位符更值钱的是跨版本对比。把上一版 PPT 的文本全部读出来存成基线这一版生成后再读一次做 diff就能看到哪些结论句发生了变化。指标口径改动导致数字整体跳变时这个 diff 是唯一能在交付前发现问题的办法。def dump_text(path): prs Presentation(path) lines [] for i, slide in enumerate(prs.slides, 1): for shape in slide.shapes: if shape.has_text_frame and shape.text_frame.text.strip(): lines.append(f[{i}] {shape.text_frame.text.strip()}) return lines base dump_text(baseline/汽车行业供应链管理.pptx) curr dump_text(out/汽车行业供应链管理.pptx) import difflib for line in difflib.unified_diff(base, curr, lineterm, n0): print(line)表头的[页码]前缀是为了让 diff 结果能直接定位到第几页否则改了三十页之后只见文字不见位置。基线版本放仓库里每次正式交付前更新一次日常迭代只跑对账不做覆盖。注意unified_diff的n0只输出变化行本身行数多的时候比默认上下文更好扫。真要看上下文时再调大。一个更省事的做法是把校验和回归串成一条命令生成完直接跑屏幕上只打印非空行——没输出就说明这一版可以直接发。把 baseline 目录留在仓库里每次交付前跑一次比在会议室里被问到再翻页强。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价