
简介这份《ERP原理与应用教程》资料面向经管、信息管理专业学生及准备ERP原理考试或认证的读者用于快速梳理ERP核心概念、计划逻辑与简答题要点。压缩包内仅1个PDF文件约37KB轻便易读便于查阅与打印。内容涵盖制造业基本方程AXB-CD、ERP从订货点法到时段式MRP、闭环MRP、MRP II直至ERP的演进以及订货点计算、安全库存、安全提前期、物料清单、工艺路线、工作中心、ABC分类与周期盘点、移动平均与指数平滑预测、主生产计划、粗能力计划、ERP软件选型等知识点题型含选择填空与简答。大题部分给出ERP实施失败原因、ERP定性效益、BOM不准确影响、预测四特征、ABC分类原理等答题框架适合期末复习与概念查漏补缺。目前已有994人学习。1. ERP原理与应用教程.pdf 真正卡人的不是原理是教程和系统对不上很多人拿到 ERP原理与应用教程.pdf 的第一反应是打开 PDF 阅读器从头读到尾读到 MRP 那一章就停了。教程里写的是「净需求 毛需求 安全库存 − 预计入库 − 现有库存」可手上的 ERP 系统里根本没有「预计入库」这个字段只有在途、待检、冻结三四种库存状态。教程给的是原理和业务流程系统给的是字段和参数中间那层翻译没人写。这篇就干这层翻译先把 PDF 解析成可检索的文本骨架再用 ERP 系统业务流程把书里的概念落成能跑的 SQL 和参数表顺手解决扫描版歪斜纠偏、pdf转word 后排版错乱、几百页找不到重点这些实际麻烦。适合正在做 erp实施 的顾问、接手老系统二次开发的程序员以及自学 ERP 原理的在校生。2. 把 ERP原理与应用教程 PDF 拆成可检索的知识骨架一份几百页的教程直接读是低效的。真正有用的是两样东西章节树和章节与 ERP 模块的对应关系。前者决定你能不能定位到「第 5 章第 3 节讲的是物料清单」后者决定你要查「提前期怎么设」时该翻哪几章。这一章先解决文本抽取再做模块打标。2.1 用 pdftotext 与 pdfplumber 抽取目录树和正文段落动手前先确认这份 PDF 是文本型还是扫描型。用 pdfinfo 看元信息用 pdfimages 看图占比两步就能判断比打开阅读器翻半天快。# 页数、是否加密、有没有字体信息没有字体记录的基本是扫描件 pdfinfo ERP原理与应用教程.pdf # 抽取前 30 页-layout 按视觉位置保留缩进目录页的行首编号才不会乱 pdftotext -layout -f 1 -l 30 ERP原理与应用教程.pdf - | head -n 120 # 看图片对象数量几十页堆了几百张图八成整本都是扫描图 pdfimages -list ERP原理与应用教程.pdf | head三个参数值得记住-f/-l限定首末页避免一次抽全本导致内存和排版双重失控-layout保留原版面空白代价是双栏排版会串行输出写-表示打到标准输出方便直接管道给 grep 或 head。文本型 PDF 走 Python 更灵活因为要做切章和清洗import pdfplumber, re, json # 只认行首的「第X章」避免正文里见第3章被误判成新章节 CHAP re.compile(r^\s*第\s*([一二三四五六七八九十\d])\s*章\s*(.{2,30})$) def split_book(path, start1, endNone): chapters, cur [], None with pdfplumber.open(path) as pdf: for i, page in enumerate(pdf.pages[start-1:end], startstart): # 中文 PDF 字距大默认容差会把一行拆成碎片调到 2~3 才连得上 text page.extract_text(x_tolerance2, y_tolerance3) or for line in text.splitlines(): m CHAP.match(line.strip()) if m: cur {no: m.group(1), title: m.group(2).strip(), start_page: i, lines: []} chapters.append(cur) continue if cur: cur[lines].append(line) for c in chapters: c[lines] [l for l in c[lines] if not re.fullmatch(r[-—\s]*\d{1,4}[-—\s]*, l)] # 去页码 c[body] \n.join(c[lines]) return chapters json.dump(split_book(ERP原理与应用教程.pdf), open(chapters.json, w, encodingutf8), ensure_asciiFalse, indent1)x_tolerance是字符横向合并容差y_tolerance是纵向换行容差这两个值调不对结果要么一行断成十几段要么两栏内容粘在一起。页码噪声用正则单独滤掉别指望它不出现——教材的页眉页脚在抽取结果里就是普通文本行。2.2 按 ERP 系统业务流程给章节打模块标签章节标题只是索引不是语义。要按业务查内容得给每章打上模块标签。最笨也最可解释的办法是关键词计分命中次数多的排前面取前两位当标签。MODULE_RULES { 销售与分销: [销售订单, 报价, 发货, 信用, 价格条件, 退货], 采购与库存: [采购申请, 采购订单, 收货, 库存, 批次, 安全库存, 盘点], 生产与计划: [物料清单, BOM, 主生产计划, MPS, MRP, 工艺路线, 工作中心, 提前期], 财务与成本: [总账, 应收, 应付, 成本中心, 凭证, 核销, 对账, 差异分摊], } def tag(chap): text chap[title] chap[body] score {m: sum(text.count(k) for k in kws) for m, kws in MODULE_RULES.items()} chap[modules] [m for m, s in sorted(score.items(), keylambda x: -x[1]) if s 0][:2] return chap关键词计分的局限很明显讲「成本核算」的章节必然大量出现「库存」会被误判到采购与库存。补救办法是给标题命中的关键词加权比如标题命中记 5 分、正文命中记 1 分两三行代码就能把准确率拉上来。模块标签典型关键词教程里对应章节类型后续用途销售与分销销售订单、发货、信用订单流程、定价与折扣设计订单状态机采购与库存采购订单、收货、批次采购流程、库存计价建订单与库存流水表生产与计划BOM、MRP、提前期物料清单、需求计划跑递归展开与净需求财务与成本凭证、核销、成本中心财务业务一体化三单匹配与对账校验打完标签写进一个 jsonl每行一章编号、标题、起始页、模块、正文。后面无论做全文检索还是喂给模型都从这份 jsonl 出不再反复解析原始 PDF。2.3 扫描版教程的 OCR、歪斜校正与中文语言包设置流传较广的 ERP原理与应用教程.pdf 不少是影印扫描件pdfplumber 抽出来一片空白这时候只能走 OCR。顺序是先转图、再纠偏、最后识别顺序颠倒会白干一遍。# 1. 300dpi 灰度转图低于 200dpi 中文识别错误率明显上升高于 400dpi 收益递减 pdftoppm -r 300 -gray -f 40 -l 60 ERP原理与应用教程.pdf page # 2. 歪斜校正扫描件常有 0.5~2 度倾斜不纠偏会把相邻两行识别成一行 # ImageMagick 自带 -deskew40% 阈值是教材类扫描件的常用起点 convert page-040.pgm -deskew 40% -threshold 60% fixed-040.pgm # 3. 中文识别需要 chi_sim 语言包--psm 6 适合整页连续正文 tesseract fixed-040.pgm stdout -l chi_sim --psm 6--psm是页面分割模式6 表示「假定整页是一块统一文本」教材正文适用表格页用 4 或 11 更准因为行列结构会被 6 抹平。识别语言必须显式写-l chi_sim不写默认按英文处理中文会输出成乱码符号。如果只需要少量章节做可编辑内容pdf转word 后手改排版是常见做法但表格和公式基本救不回来还得回到 OCR 文本上校对。提示OCR 结果必须抽样人工校对重点看数字。物料号、金额、提前期这类数字错一位后面建的数据模型全歪而且很难发现。一套 OCR 加纠偏脚本跑完教程正文就变成了可 grep 的文本。前端要展示的话用 vue 页面配 elementui 弹窗加载 PDF 预览就能满足查阅需求真正的检索还是在 jsonl 或后面的全文索引里做。3. 用 ERP 系统业务流程把教程概念落成最小可跑的表结构原理层面的公式谁都会背能不能落地要看表结构撑不撑得住。这一章按教程的业务顺序把物料、BOM、订单、库存、需求计划五块做成一套能跑 SQL 的最小模型。表不用多几张就够暴露大部分设计问题。3.1 物料主数据与 BOM 递归展开的 SQL 写法先建两张表物料和物料清单。字段不多但每个字段都对应教程里的一个概念缺一个后面就算不出正确结果。CREATE TABLE item ( item_code VARCHAR(32) PRIMARY KEY, item_name VARCHAR(64) NOT NULL, item_type CHAR(1) NOT NULL, -- M自制 P采购 F虚拟件 uom VARCHAR(8) NOT NULL, -- 基础单位必须唯一禁止双单位混存 lead_time SMALLINT DEFAULT 0, -- 提前期天 safety_stock NUMERIC(14,3) DEFAULT 0, -- 安全库存 lot_rule VARCHAR(8) DEFAULT LFL -- LFL 逐批 / FOQ 固定批量 / POQ 周期批量 ); CREATE TABLE bom ( parent_code VARCHAR(32) REFERENCES item(item_code), child_code VARCHAR(32) REFERENCES item(item_code), qty_per NUMERIC(14,6) NOT NULL, -- 单位用量按子件基础单位 scrap_rate NUMERIC(6,4) DEFAULT 0, -- 损耗率 eff_from DATE NOT NULL, eff_to DATE, PRIMARY KEY (parent_code, child_code, eff_from) );BOM 展开用递归 CTE关键是生效日期过滤必须写在递归里面WITH RECURSIVE explode AS ( SELECT b.parent_code, b.child_code, b.qty_per, 1 AS lvl, b.qty_per * (1 b.scrap_rate) AS total_qty FROM bom b WHERE b.parent_code :root AND CURRENT_DATE BETWEEN b.eff_from AND COALESCE(b.eff_to, DATE 9999-12-31) UNION ALL SELECT b.parent_code, b.child_code, b.qty_per, e.lvl 1, e.total_qty * b.qty_per * (1 b.scrap_rate) FROM bom b JOIN explode e ON b.parent_code e.child_code WHERE e.lvl 12 AND CURRENT_DATE BETWEEN b.eff_from AND COALESCE(b.eff_to, DATE 9999-12-31) ) SELECT lvl, child_code, total_qty FROM explode ORDER BY lvl, child_code;三个点必须说清楚。total_qty累乘时带上(1 scrap_rate)这是教程写了但很多自建系统漏掉的一步漏掉就会长期缺料lvl 12是防环的兜底真实数据里 A→B→A 的环形 BOM 并不罕见加了层级上限只保证不死循环不保证结果正确所以还得单独跑一遍环检测生效日期写进递归体否则历史版本 BOM 会污染展开结果算出来的用量看着合理但完全对不上当前工程。3.2 采购—入库—应付、销售—出库—应收的核销表设计订单和库存流水两张表是整个 ERP 里复用度最高的结构。采购和销售共用一套订单表用order_type区分出入库共用一套流水表用txn_type区分。CREATE TABLE biz_order ( order_no VARCHAR(24) PRIMARY KEY, order_type VARCHAR(4) NOT NULL, -- PO 采购 / SO 销售 partner_code VARCHAR(16) NOT NULL, order_date DATE NOT NULL, qty NUMERIC(14,3) NOT NULL, price NUMERIC(14,4) NOT NULL, status VARCHAR(8) DEFAULT OPEN -- OPEN 未执行 / PART 部分 / DONE 完成 ); CREATE TABLE stock_txn ( txn_id BIGSERIAL PRIMARY KEY, order_no VARCHAR(24) REFERENCES biz_order(order_no), txn_type VARCHAR(6) NOT NULL, -- RECV 收货 / ISSUE 发货 / RETURN 退货 qty NUMERIC(14,3) NOT NULL, txn_time TIMESTAMP NOT NULL );有了这两张表教程里反复讲的「订单执行率」「未结订单」就是一个查询SELECT o.order_no, o.qty AS order_qty, COALESCE(SUM(t.qty), 0) AS done_qty, o.qty - COALESCE(SUM(t.qty), 0) AS open_qty FROM biz_order o LEFT JOIN stock_txn t ON t.order_no o.order_no AND t.txn_type RECV WHERE o.order_type PO AND o.order_date DATE 2024-01-01 GROUP BY o.order_no, o.qty HAVING o.qty - COALESCE(SUM(t.qty), 0) 0 ORDER BY open_qty DESC;LEFT JOIN加COALESCE是为了让一笔货都没收的订单也能出现用内连接会把最需要跟催的那批订单直接过滤掉这是新手最常见的错。HAVING把已执行完的行剔掉结果直接就是跟催清单。再加一张发票表把发票金额和订单金额比对差异在容差内自动放行这就是三单匹配的最小可用版本。教程概念对应字段常见误用基本计量单位item.uom用双单位存数量换算全靠代码损耗率bom.scrap_rate只在报表里体现不参与用量计算订单状态biz_order.status靠数量推导状态状态字段长期不更新已分配量stock_txn 中未发货行可用库存不减已分配导致超卖3.3 用教程里的实训数据跑一遍 MRP 净需求净需求公式本身很简单难的是时间分桶和可用量口径。整段汇总算出来的需求没有意义必须按周或按日分桶。import math def net_requirement(gross, on_hand, scheduled_in, allocated, safety_stock, lot_ruleLFL, lot_size0): 单桶净需求gross 毛需求on_hand 现有库存scheduled_in 预计入库 allocated 已分配未出库lot_rule 批量规则 available on_hand - allocated # 可用量必须先扣已分配 net gross safety_stock - available - scheduled_in if net 0: return 0 if lot_rule LFL: return net if lot_rule FOQ and lot_size 0: # 固定批量向上取整到批量倍数 return math.ceil(net / lot_size) * lot_size return neton_hand - allocated这一步是工程和教科书的第一个分叉点书里默认现有库存全部可用实际系统里被订单占住的部分不能重复承诺。第二个分叉点是批量规则LFL 逐批下单一分不多库存最干净但采购次数最多FOQ 按供应商最小包装量取整采购省事但会堆积库存。生产环境通常是 A 类料用 LFL、C 类料用 FOQ按 ABC 分类批量刷一遍参数比全套用同一种规则靠谱得多。分桶的实现交给数据库更省事按需求的到货日期date_trunc(week, due_date)分组把每桶的毛需求汇总后逐桶调用上面的函数前一周的预计入库和库存结余结转到下一周。结转逻辑写错了净需求会周期性出现负数和虚高先在教程的实训数据上跑通再上真实数据。4. ERP 实施阶段教程里的参数到生产环境的映射教程的最后一章通常讲实施方法论但方法论落不到参数上就是空话。ERP 实施最难的不是流程梳理是把教科书口径的参数换算成系统口径这一步做错上线三个月后必然出现大量手工作业回补。这一章按主数据、计划参数、上线核对三个环节讲。4.1 编码规则与主数据清洗物料编码是全部数据的根。常见做法是定长分段编码比如 4 位分类 4 位顺序 2 位属性总长不超过 20 位纯数字或大写字母加数字不嵌中文、不嵌供应商名、不嵌部门。编码里嵌了语义日后分类调整就要重编而重编物料编码在运行中的系统里几乎是不可能的任务。从教程附录或实训数据导入物料表第一步永远是清洗import pandas as pd df pd.read_excel(item_from_course.xlsx, dtypestr) df[item_name] (df[item_name].str.strip() .str.replace(r\s, , regexTrue) .str.replace(r[(].*?[)], , regexTrue)) # 去规格后缀 # 单位归一化同一个东西绝不允许出现两种写法 UOM_MAP {个: PCS, 件: PCS, 只: PCS, 公斤: KG, 千克: KG, 吨: TON} df[uom] df[uom].map(UOM_MAP).fillna(df[uom]) dups df[df.duplicated(item_name, keepFalse)].sort_values(item_name) print(dups[[item_code, item_name, uom]].to_string(indexFalse))最后那段查重是重点名称相同、编码不同的物料几乎每个导入的项目都会出现通常来自不同部门各自建档。keepFalse让所有重复行都显示出来方便逐组判断是合并还是加规格后缀区分。清完再导比导进去之后靠人工在系统里找要省几十倍时间。4.2 计划参数提前期、安全库存与批量规则的设置口径计划参数是教程和系统差异最大的地方。表格里列的几项每一项都要明确口径。参数教程口径系统常见口径起手取值建议提前期工作日自然日或工作日看配置先按自然日观察两个月再调安全库存Z × 需求标准差 × √提前期固定值或公式字段先给近 3 个月出库标准差的 1.65 倍批量规则逐批、固定批量、周期批量常带最小包装量和倍数取整A 类料 LFLC 类料 FOQ计划时界概念性描述需求时界 计划时界两段需求时界 2 周计划时界 8 周安全库存用统计口径算比拍脑袋强一条 SQL 能批量刷UPDATE item SET safety_stock CEIL(1.65 * s.sigma_out * SQRT(GREATEST(item.lead_time, 1))) FROM ( SELECT item_code, STDDEV(weekly_out) AS sigma_out FROM outbound_weekly GROUP BY item_code ) s WHERE item.item_code s.item_code AND item.safety_stock 0; -- 只刷未设置过的不覆盖人工调整过的值1.65对应约 95% 的服务水平要更高服务水平就换 2.3399%。SQRT(lead_time)体现的是提前期内需求波动的叠加提前期按天算、出库按周算的时候必须先把单位对齐这一步错了安全库存会差 2.6 倍。WHERE safety_stock 0这个条件是保护人工判断批量刷参数最忌讳全覆盖把业务已经调优过的值冲掉比不刷还糟。4.3 上线前必须回算的几张核对表上线前跑不通的查询上线后一定会变成手工台账。以下几条建议做成固定核对脚本每天跑一次。-- 1. 叶子件必须是采购件否则 MRP 会算出没有供应来源的需求 SELECT b.child_code FROM bom b JOIN item i ON i.item_code b.child_code WHERE i.item_type M AND NOT EXISTS (SELECT 1 FROM bom x WHERE x.parent_code b.child_code); -- 2. 库存与总账差异超过容差的全部拉出来 SELECT i.item_code, SUM(t.qty * i.std_cost) AS stock_amount FROM stock_txn t JOIN item i ON i.item_code t.item_code GROUP BY i.item_code HAVING ABS(SUM(CASE WHEN t.txn_type RECV THEN t.qty ELSE -t.qty END) * MAX(i.std_cost)) 1000;第一条查的是「有父无子」的自制件MRP 展开到这里就断了需求挂在一个自己生产但没有任何下层物料的节点上计划员只能手工补单。第二条查库存金额与财务总账的差异RECV记正、其余记负是一种简化记账真实系统里要按移动类型分别处理容差取 1000 元只是示例实务中按物料金额分布定。试算 MRP 与人工计划的偏差是最有价值的一次核对拿最近一个月的真实需求跑一遍净需求和计划员手上的排产单逐条比。偏差超过 20% 的物料八成是提前期或批量规则没配对趁上线前改成本最低。5. 进阶把教程 PDF 做成离线可检索知识库并对账校验教程拆成 jsonl 之后检索可以做得更工程化。用 SQLite 的 FTS5 建全文索引比每次 grep 快一个数量级而且能在同一张表里带上模块标签和页码。CREATE VIRTUAL TABLE doc_fts USING fts5( chap_no, title, modules, body, start_page, tokenizeunicode61 -- 中文按字切够用但不理想 ); -- 更好的做法先用 jieba 分词把正文存成空格分隔检索命中率明显提升查询时用MATCH加模块过滤比如找「安全库存」的算法说明SELECT chap_no, title, start_page FROM doc_fts WHERE doc_fts MATCH modules:采购与库存 AND body:安全库存 ORDER BY rank LIMIT 10;rank是 FTS5 的内置相关性排序不用自己算权重。用 jieba 预分词的话把「安全库存」当整体词写入比按字切再拼要准得多代价是索引体积变大、需要维护一份分词脚本。财务侧的进阶用法是把 PDF 教程学到的对账逻辑直接用在发票 PDF 上。很多企业的应付明细还是从供应商发来的 pdf发票 里人工录的用 pdfplumber 提取号码、金额、税额再和 ERP 里的应付单据做本地对账差异行直接落表import pdfplumber, re, decimal NUM re.compile(r(?:价税合计|合计金额)[^\d]{0,8}([\d,]\.\d{2})) def read_invoice(path): with pdfplumber.open(path) as pdf: text \n.join(p.extract_text() or for p in pdf.pages) inv_no re.search(r发票号码[:]?\s*(\d{8,20}), text) amount NUM.search(text) return { inv_no: inv_no.group(1) if inv_no else None, amount: decimal.Decimal(amount.group(1).replace(,, )) if amount else None, }比对时用decimal.Decimal而不是浮点金额用 float 做相等判断迟早出错容差按绝对值 1 元或千分之五取小超出容差的进人工复核队列。这套本地校验不依赖任何外部服务跑在财务自己的机器上先把重复发票号和金额不符两类拦下来就能覆盖大部分录入错误。本文还有配套的精品资源点击获取