简介:面向制造企业质量、生产、物流、EHS、采购与财务岗位的报废品处理流程指导文件,可用于规范采购品、外协品、半成品及成品的报废确认、退库、仓储、出售与最终处置,帮助企业在成本控制、资源回收和环保合规之间取得平衡。资源包内含1个PDF文件,大小约159KB,篇幅精炼,便于直接查阅、打印或作为内部培训与制度修订参考。全文围绕目的、适用范围、权责分配、名词定义和作业内容展开,逐项界定财务、采购、EHS、质量、制造、设备工程、计划与物流等部门的职责,并梳理不合格品评审单、报废品处理单、IQC检验、MRB会议、废品仓库入账标识等关键操作。还涉及报废品出售询议价、财务审批、过磅确认、EHS放行、仓库暂存15天、记录保存一年可追溯及流程图示,覆盖跨部门协作要点。已有93人学习,可供质量体系与生产运营人员借鉴。
1. 报废品处理流程文件躺在共享盘,为什么现场还是不按它做
车间公告栏上那份《报废品处理流程》贴了两年,共享盘里还有一份报废品处理流程,指导书可用.pdf,但真正出问题时,追溯靠的是班组长回忆和一本翻烂的纸质台账。上个月一批外观不良的注塑件被判成待检品重新上线,客户投诉后才倒查出来:文件里写的判定标准、隔离时限、审批层级都对,唯独没有任何一处能被系统读取,也没人知道手上那份 PDF 到底是 Rev.B 还是 Rev.C。
这个标题真正要解决的不是"再写一份更漂亮的指导书",而是让报废品处理流程从一份静态 PDF,变成一套可校验、可审批、可对账的线上流程,并且让导出的指导书本身"可用"——现场扫码能带出单据,版本号能一眼分辨,字段能直接进台账。适合品质工程、制造 IT、MES/ERP 实施和做数字化车间的开发者,尤其是那些已经有一堆 Word 和 PDF 规程、但流程依然靠人盯的团队。
2. 把报废品处理流程拆成可执行的状态机与字段模型
一份报废品指导书里最容易被忽略的部分,是它其实描述了一个状态流转过程:谁判定、谁审批、实物去哪、账什么时候关。这一步不做结构化,后面所有系统化都是空谈。
2.1 报废品处理流程的六个状态与每个状态的流转边界
常见做法是把报废品处理流程收敛成六个状态,每个状态只有一个明确的责任角色和一组允许操作。状态多了现场记不住,少了又会出现"批了但没入报废库"这种账实不符。
| 状态 | 责任角色 | 允许操作 | 超时阈值 | 超时动作 |
|---|---|---|---|---|
| 待判定 | 现场班组长 | 发起、补照片 | 4 小时 | 提醒品质 |
| 待审批 | 品质工程师 | 通过、驳回 | 24 小时 | 升级至品质主管 |
| 已批准 | 仓储 | 打印报废单、转移 | 8 小时 | 卡住下一工单领料 |
| 已入报废库 | 仓储 | 分区上架、登记 | 72 小时 | 日报预警 |
| 已处置 | 处置方 | 拆解/变卖/销毁登记 | 7 天 | 财务挂账提醒 |
| 已关账 | 财务 | 只读 | — | 不可再改 |
这张表的价值在于,它把指导书正文里那些"应及时""原则上"的模糊表述,换成了可配置的数值。阈值不是拍脑袋定的,取的是过去三个月单据流转时间的 P90,先设成 P90,跑一个月再收紧。
2.2 字段模型:从指导书正文里抽出的必填项
字段模型要覆盖"判定、隔离、审批、处置、入账"五个环节,缺任何一个都会在事后追溯时断链。
| 字段 | 类型 | 必填 | 取值来源 | 说明 |
|---|---|---|---|---|
| scrap_no | string(20) | 是 | 系统生成 | 报废单号,规则 SQ+车间码+年月日+4 位流水 |
| request_id | string(36) | 是 | 客户端生成 | 幂等键,防止重复提交重复扣账 |
| material_code | string(30) | 是 | MES 工单 | 物料编码 |
| qty | decimal(12,3) | 是 | 现场录入 | 按基本计量单位,禁止用箱/托 |
| defect_code | string(10) | 是 | 品质判定 | 关联不良项字典 |
| scrap_reason | string(200) | 是 | 现场+品质 | 指导书附录 B 有填写范例 |
| station | string(20) | 是 | 工位主数据 | 发生工位,用于责任归属 |
| owner_dept | string(20) | 是 | 组织主数据 | 决定审批分级 |
| dispose_way | enum | 条件必填 | 指导书附录 A | 拆解/变卖/销毁/退供 |
| evidence_url | string(255) | 条件必填 | 拍照上传 | 金额超阈值时必须附证据 |
request_id这一列值得单独说。车间网络抖一下,扫码枪重复触发两次,重复的报废单会直接造成库存多扣。把幂等键做成必填,是最便宜的一道防线。
2.3 用 Python 落一个可单测的状态机
状态机不要写在 UI 里,写成独立模块,这样加签、撤回、升级这些规则才测得动。
from enum import Enum from datetime import datetime, timedelta class ScrapState(Enum): DRAFT = "待判定" REVIEW = "待审批" APPROVED = "已批准" IN_STORE = "已入报废库" DISPOSED = "已处置" CLOSED = "已关账" # 只允许这些迁移,其余一律拒绝 TRANSITIONS = { ScrapState.DRAFT: [ScrapState.REVIEW], ScrapState.REVIEW: [ScrapState.APPROVED, ScrapState.DRAFT], # 驳回回到待判定 ScrapState.APPROVED: [ScrapState.IN_STORE], ScrapState.IN_STORE: [ScrapState.DISPOSED], ScrapState.DISPOSED: [ScrapState.CLOSED], ScrapState.CLOSED: [], } def transition(order, to_state, operator, role): if to_state not in TRANSITIONS[order.state]: raise ValueError(f"非法流转 {order.state.value} -> {to_state.value}") if not can_operate(role, order.state, to_state): raise PermissionError(f"角色 {role} 无权执行该流转") order.state = to_state order.trail.append((datetime.now(), operator, to_state.value)) if to_state is ScrapState.IN_STORE: # 入报废库才扣库存,早扣晚扣都容易错 order.freeze_inventory() # 冻结而非直接扣减,等关账再落账 return order def can_operate(role, cur, nxt): RULES = { (ScrapState.DRAFT, ScrapState.REVIEW): {"班组长", "品质工程师"}, (ScrapState.REVIEW, ScrapState.APPROVED): {"品质工程师", "品质主管"}, (ScrapState.REVIEW, ScrapState.DRAFT): {"品质工程师", "品质主管"}, (ScrapState.APPROVED, ScrapState.IN_STORE): {"仓储"}, (ScrapState.IN_STORE, ScrapState.DISPOSED): {"仓储", "处置方"}, (ScrapState.DISPOSED, ScrapState.CLOSED): {"财务"}, } return role in RULES.get((cur, nxt), set())TRANSITIONS是唯一事实来源,UI 上的按钮显隐也从它推导,避免出现"按钮能点但后端拒绝"的割裂。freeze_inventory选择冻结而不是直接扣减,是因为报废在关账前仍有被撤回的可能,先扣会导致月底对账反复调整。trail记录每次流转的时间、操作人和目标状态,这就是指导书里"记录保存三年"要求的落地方式。
2.4 参数怎么定:审批分级、金额阈值与超时兜底
审批分级不要按人,按金额和部门两个维度交叉定,规则写死在配置表里而不是代码里,后续改阈值不用发版。
- 单张报废金额小于 500 元:品质工程师一级审批。
- 500 到 5000 元:加品质主管二级审批。
- 超过 5000 元或涉及安全件:加制造经理、财务会签,且必须附证据照片。
- 超时处理:
待审批超过 24 小时自动升级并给主管推送,48 小时未处理则锁死该工单的下一道领料,逼现场来处理。
阈值上线后要回看两个指标:一是审批时长中位数,超过 12 小时说明层级还是太重;二是驳回率,高于 15% 通常是填写指引不清,该改的是指导书的范例部分,不是加审批人。
3. 生成一份真正"可用"的指导书 PDF:版式、条码与版本校验
指导书可用.pdf这个后缀其实提了要求:这份 PDF 不只是给人看的,还要能被扫码枪读、能被系统比对、能证明自己是最新版。
3.1 可读、可扫、可追溯三条硬指标
可读指 A4 单面能放下判定标准和填写范例,现场不用翻页;可扫指每张报废单带唯一条码,扫码直接带出单据状态;可追溯指页脚有版本号、生效日期和文件哈希。三条缺一条,指导书就会退回成墙上的装饰。很多团队只做到了第一条,结果现场拿的还是旧版,流程判定按旧标准走。
3.2 用 reportlab 生成带条码与版本号的指导书
# gen_work_instruction.py from reportlab.lib.pagesizes import A4 from reportlab.lib.units import mm from reportlab.pdfgen import canvas from reportlab.graphics.barcode import code128 DOC_VERSION = "WI-QA-014 Rev.C" # 正文任何一处改动都必须递增,禁止原地覆盖 EFFECTIVE_DATE = "2024-07-01" def build_pdf(path, fields): c = canvas.Canvas(path, pagesize=A4) w, h = A4 # 页眉固定版本号和生效日期,现场一眼分新旧 c.setFont("Helvetica-Bold", 14) c.drawString(20*mm, h - 20*mm, fields["title"]) c.setFont("Helvetica", 9) c.drawString(20*mm, h - 26*mm, f"{DOC_VERSION} 生效日期 {EFFECTIVE_DATE}") # 正文按 95 个字符折行,避免长句被页边距截断 text = c.beginText(20*mm, h - 40*mm) text.setFont("Helvetica", 10) for line in fields["body"]: text.textLine(line) c.drawText(text) # 右下角贴单号条码,扫码即带出流程状态 bc = code128.Code128(fields["scrap_no"], barHeight=12*mm, barWidth=0.3*mm) bc.drawOn(c, w - 85*mm, 20*mm) c.setFont("Helvetica", 8) c.drawString(w - 85*mm, 16*mm, fields["scrap_no"]) c.save()DOC_VERSION和EFFECTIVE_DATE必须是常量而不是运行时取值,否则同一份内容每次生成出来的版本号都不一样,比对就失去意义。barHeight取 12mm、barWidth取 0.3mm,是车间常见标签打印机和扫码枪都能稳定识读的下限,低于这个尺寸在油污或覆膜后误读率明显上升。正文折行宽度按 95 字符控制,是为了让判定标准段落不跨页。
3.3 版本哈希:确保现场拿到的一定是最新那份
# 发布时生成哈希清单,随文件一起下发 sha256sum 报废品处理流程_WI-QA-014_RevC.pdf > WI-QA-014_RevC.sha256 # 现场公告机或平板开机自检:哈希不符说明是旧版或被改过 sha256sum -c WI-QA-014_RevC.sha256 || echo "版本异常,请重新下发"哈希清单要和服务端存的那份一致,这样"现场拿的版本对不对"就不再靠人问人。把这段脚本挂到公告机开机自启里,比每周派人去各车间核对文件有效期省事得多。
3.4 条码与二维码的关键参数对照
| 参数 | 推荐值 | 下限 | 影响 |
|---|---|---|---|
| barWidth | 0.33mm | 0.25mm | 过细时喷墨打印会糊,扫描失败 |
| barHeight | 12mm | 8mm | 过矮时扫码枪倾斜角度稍大就读不到 |
| quiet zone | 6mm | 3mm | 留白不足会导致首尾字符误读 |
| 二维码纠错等级 | M | L | 现场有油污建议提到 Q |
| 打印分辨率 | 300dpi | 203dpi | 低于 203dpi 条码边缘会毛刺 |
条码内容建议直接放scrap_no,不要塞 JSON,长度一长条码密度就上去了,识读率下降得很快。需要在扫码后带出更多信息,让扫码端拿单号去查接口。
4. 报废品处理流程对接 MES/ERP:扫码报废、审批与台账对账
指导书管的是"人怎么判",系统管的是"账怎么走"。这两者接不上,流程就永远有两套记录。
4.1 扫码报废接口的契约与幂等设计
接口只做一件事:把现场扫到的单号和判定结果落库,并返回当前状态。幂等键用客户端生成的request_id,服务端先查后写。
def post_scrap(cur, payload, request_id): """报废接口:request_id 幂等,重复提交只落一条,不重复冻结库存""" cur.execute("SELECT scrap_no, state FROM scrap_order WHERE request_id = %s", (request_id,)) row = cur.fetchone() if row: return {"scrap_no": row[0], "state": row[1], "dup": True} scrap_no = next_scrap_no(cur, payload["station"]) # SQ + 车间码 + 日期 + 流水 cur.execute(""" INSERT INTO scrap_order (scrap_no, request_id, material_code, qty, defect_code, scrap_reason, station, owner_dept, state, created_at) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, '待判定', now()) """, (scrap_no, request_id, payload["material_code"], payload["qty"], payload["defect_code"], payload["scrap_reason"], payload["station"], payload["owner_dept"])) return {"scrap_no": scrap_no, "state": "待判定", "dup": False}next_scrap_no的流水号必须走数据库序列或带行锁的计数表,不能用时间戳拼接,否则并发扫码会撞号。dup字段返回给扫码端,让操作员看到"这张单刚才已提交",比抛异常友好得多。整个接口不直接扣库存,只写单据,库存动作留给状态机在入报废库时触发。
4.2 报废台账表结构与必须建的索引
CREATE TABLE scrap_order ( id BIGSERIAL PRIMARY KEY, scrap_no VARCHAR(20) NOT NULL UNIQUE, request_id VARCHAR(36) NOT NULL UNIQUE, material_code VARCHAR(30) NOT NULL, qty NUMERIC(12,3) NOT NULL CHECK (qty > 0), defect_code VARCHAR(10) NOT NULL, scrap_reason VARCHAR(200) NOT NULL, station VARCHAR(20) NOT NULL, owner_dept VARCHAR(20) NOT NULL, dispose_way VARCHAR(10), evidence_url VARCHAR(255), state VARCHAR(16) NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), closed_at TIMESTAMPTZ ); -- 车间看板按工位+状态过滤,这条索引撑住 90% 的查询 CREATE INDEX idx_scrap_station_state ON scrap_order (station, state); -- 月末按部门出报废汇总 CREATE INDEX idx_scrap_dept_created ON scrap_order (owner_dept, created_at DESC);qty加CHECK (qty > 0)是防呆:报废数量为负或零在业务上无意义,能在库层挡住的错误不要留到报表阶段才发现。state用字符串而不是数字枚举,运维排查时肉眼可读,代价只是几个字节。closed_at单独一列,方便算处置周期。
4.3 审批通过与库存冻结的时序问题
审批和库存动作分属两个系统,最容易踩的坑是"审批通过了但冻结失败",导致实物已经隔离、账面还有库存。常见做法是把冻结做成带重试的异步任务,并在报废单上记录冻结状态。
def on_approved(order, cur, mq): # 先落本地状态,再发消息去触发冻结,失败可重放 cur.execute("UPDATE scrap_order SET state='已批准', freeze_status='PENDING' " "WHERE scrap_no=%s AND state='待审批'", (order.scrap_no,)) if cur.rowcount == 0: # 乐观锁:别处已改过状态,直接放弃本次 return mq.publish("scrap.freeze", {"scrap_no": order.scrap_no, "material_code": order.material_code, "qty": str(order.qty)})WHERE state='待审批'是乐观锁,两个审批人同时点通过时,只有一个能更新成功。freeze_status字段让运维能一眼找出卡在中间态的单据:PENDING超过 10 分钟的就是失败任务,直接重放消息即可,不需要人工去改库存。
4.4 一条 SQL 找出账实不符的报废单
月末对账最实用的查询,是把"已入报废库但库存未冻结"和"已关账但处置未登记"两类异常捞出来。
SELECT scrap_no, material_code, qty, state, freeze_status, now() - created_at AS aging FROM scrap_order WHERE (state IN ('已入报废库','已处置') AND freeze_status <> 'DONE') OR (state = '已关账' AND dispose_way IS NULL) ORDER BY aging DESC LIMIT 200;aging用来排优先级,挂得越久越可能是流程断点。把这条查询做成每日定时任务,结果直接推给品质主管,比等财务月底发现差异再回头翻单子高效得多。freeze_status和dispose_way这两个字段看起来是冗余的,实际是排错时最省钱的两个抓手。
5. 让指导书可检索:现场排错、参数基线与复盘查询
指导书最大的浪费是写完就锁进 PDF。把正文按章节切成结构化条目存起来,现场遇到"安全件报废要不要会签"这类问题,扫码或搜关键词就能直接命中条款,而不是翻到第 7 页找。
import re, json def parse_instruction(pdf_text): """把指导书正文拆成条款级条目,便于按关键词检索""" sections = re.split(r"\n(?=\d+(\.\d+)*\s)", pdf_text) # 按 1、1.1 这样的编号切 items = [] for s in sections: m = re.match(r"(\d+(\.\d+)*)\s+(.+)", s.strip()) if not m: continue no, _, rest = m.groups() body = rest.split("\n", 1)[1] if "\n" in rest else "" items.append({ "clause": no, "title": rest.split("\n")[0][:60], "body": body.strip(), "doc_version": DOC_VERSION, # 条款要绑版本,否则新旧混检 }) return itemsclause保留原始编号,是为了让检索结果能精确回指到 PDF 的章节;doc_version必须一起存,否则改版后旧条款还留在索引里,现场搜出来的可能是已经作废的判定标准。切分正则按数字编号走,前提是指导书正文本身编号规范,这也是写指导书时该守的一条纪律。
现场排错时,最常被问到的参数其实就三个,建议直接做成基线卡片贴在工位:
| 排错现象 | 先看哪个参数 | 常见取值 | 处理动作 |
|---|---|---|---|
| 扫码报废无响应 | request_id 是否重复 | 36 位 UUID | 让扫码端重新生成后重试 |
| 审批卡住 | 审批层级与金额阈值 | 500 / 5000 元 | 核对 owner_dept 是否填错 |
| 库存账实不符 | freeze_status | PENDING / DONE | 重放冻结消息,不手工改库存 |
| 条码扫不出 | barWidth | 0.33mm | 重新生成并提高打印分辨率 |
| 搜不到条款 | doc_version | 当前 Rev | 重建索引,清掉旧版本条目 |
复盘查询可以固定在每周一跑一次,把上周所有跨越三个以上状态、且总时长超过 7 天的单据捞出来,逐条看卡在哪一步。多数时候问题不在审批人,而在owner_dept填错导致审批路由到了不相干的部门,这类数据质量问题从时长分布上看得最清楚。把aging超过 7 天的单据和它的流转轨迹trail一起导出,就是下一次修订指导书最扎实的输入。
本文还有配套的精品资源,点击获取