首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >如何开发ERP(离散制造-MTO)系统中的采购管理板块(附架构图+流程图+代码参考)

如何开发ERP(离散制造-MTO)系统中的采购管理板块(附架构图+流程图+代码参考)

原创
作者头像
用户11735492
发布2025-09-19 18:52:50
发布2025-09-19 18:52:50
530
举报

离散制造(Make-to-Order,MTO)场景对采购的要求很高——订单触发采购、交期紧、零件多、供应商管理复杂、退货与替换频繁。

采购管理不是“买东西”的流程,而是制造计划与供应商生态的接口,是控制成本、保证交期与质量、降低库存占用的关键模块。

因此做一个专门针对 MTO 的采购管理板块,不只是把振铃串起来,更要把审批、需求预测、入库/退货、执行看板、统计分析结合起来,形成闭环。

本文你将了解

  1. 什么是 ERP(离散制造-MTO)系统
  2. 高层架构图(组件说明)
  3. 数据模型与关键表设计
  4. 采购业务流程与流程图
  5. 功能模块拆解
  6. 开发技巧与工程实践
  7. 参考实现
  8. 前端看板与实现建议
  9. 交付效果与 KPI

注:本文示例所用方案模板:简道云ERP系统,给大家示例的是一些通用的功能和模块,都是支持自定义修改的,你可以根据自己的需求修改里面的功能。


一、到底什么是 ERP(离散制造-MTO)系统?

简单说:ERP 是一套把企业各类业务(销售、生产、采购、库存、财务、人力)联动起来的软件。

离散制造-MTO 强调“订单驱动生产”——每个生产/采购动作通常始于客户订单或工程变更。

对采购模块的要求包括快速响应订单、按序列号追踪、支持回写到 WIP(在制品)和质量检验、以及动态供应商切换与交期预测。


二、主要功能

  • 采购管理:采购计划、采购单生命周期管理(草稿 → 审批 → 下发 → 收货 → 结算)
  • 供应商管理:供应商档案、资质、交期与价格条目、评分/绩效
  • 采购申请(PR):由生产/工程/销售触发,支持物料明细、优先级、预计到货日
  • 采购订单(PO):创建、审批、发给供应商、变更与取消
  • 采购入库:验收流程、质检结果回写、批次/序列号记录
  • 采购退货:供应商退货、费用记账、质检与补单流程
  • 采购需求统计:基于订单/工程变更的需求汇总(按物料、交期)
  • 采购订单统计:成交率、按供应商/物料统计(周期、金额)
  • 采购执行跟踪看板:实时展示关键指标(未交、逾期、到货率、分供应商情况)

非功能要求

  • 高可靠性(事务/补偿机制)
  • 可追溯(审计日志、每条记录的操作者/时间)
  • 可扩展(支持多个工厂、多个仓库)
  • 接口能力(与 MES、WMS、财务、SRM 集成)
  • 支持并发:MTO 环境下大量 PR/PO 并发创建

三、架构图

下面给一个简洁的微服务/模块化架构图(ASCII),便于项目初期沟通:

代码语言:txt
复制
+---------------------------+       +---------------------+
|   前端应用(React/PC)    | <-->  | API Gateway / BFF   |
+---------------------------+       +---------------------+
             |                                 |
             v                                 v
+--------------------------------------------------------------+
|                 采购管理微服务 / 模块 (Procurement)          |
|  - REST API                                                   |
|  - 审批引擎(可插拔)                                         |
|  - 统计与报表服务                                             |
|  - 看板(WebSocket / Push)                                   |
+--------------------------------------------------------------+
     |        |           |              |               |
     v        v           v              v               v
+--------+ +--------+ +--------+  +--------------+  +-------------+
| DB (订单) | | DB(库存)| | SRM/供应商| | MQ (异步流程)|  | Search/BI |
+--------+ +--------+ +--------+  +--------------+  +-------------+
     |           |              |              |
     v           v              v              v
  MES/WMS   财务系统        供应商门户      第三方物流/邮件/ERP

说明:

  • 采购模块需与库存(WMS)、生产(MES)、财务、SRM(供应商关系管理)和外部供应商门户打通。
  • 使用消息队列(Kafka/RabbitMQ)处理异步通知(如发邮件、发供应商通知、到货事件)。
  • 看板使用 WebSocket 或 Server-Sent Events 推实时数据。

四、数据模型

核心实体:

  • Supplier(供应商): id, name, code, contact, lead_time_mean, rating, status, qualification_docs
  • Material(物料): id, sku, description, uom, mm_class
  • PurchaseRequest(PR): id, requester, source_type(Sales/Production/Enginee), created_at, status
  • PurchaseRequestLine: pr_line_id, pr_id, material_id, qty, required_date, priority
  • PurchaseOrder(PO): id, po_no, supplier_id, created_by, status, total_amount, currency, delivery_terms
  • PurchaseOrderLine: po_line_id, po_id, material_id, qty_ordered, qty_received, unit_price, promised_date
  • Receipt(收货): id, po_id, receipt_no, warehouse_id, qty, batch_no, inspection_status
  • Return(退货): id, po_id, reason, qty, status
  • AuditLog: entity_type, entity_id, action, actor, payload, timestamp

在 ER 图中 PurchaseRequest → PurchaseRequestLine;PR 转化为 PO 的过程会创建 PurchaseOrder 与 PurchaseOrderLine 并保留引用关系(traceability)。


五、采购业务流程

  1. 需求产生(销售订单/生产订单/工程变更/库存最低触发)→ 形成 PR(采购申请)
  2. PR 审核(自动或人工)→ 通过后转 PO(可以合并多条 PR 到一张 PO)
  3. PO 发出给供应商(邮件/SRM)→ 供应商确认(回签)
  4. 供应商发货 → 入库前验收(到货通知)→ 质检(合格/不合格)
  5. 收货成功后,更新库存与在制品(WIP)并触发财务凭证
  6. 若不合格,发起退货流程,供应商返修或退货并结算差价
  7. 统计与看板:展示交期达成率、逾期 PO、供应商绩效

流程图:

代码语言:txt
复制
[需求触发] --> [创建 PR] --> [PR 审批?]
                               |yes
                               v
                         [合并/生成 PO] --> [下发供应商] --> [供应商发货]
                                                           |
                                                           v
                                                     [到货/验收] --合格--> [入库更新库存/财务]
                                                           |
                                                          不合格
                                                           v
                                                      [发起退货/索赔] --> [退货/补发/结算]


六、功能模块拆解

  • 供应商管理

支持多联系人、多价格表(按物料/数量折扣)、资质文档(文件存储和过期提醒)

供应商评分(按准时率、质量、不良率、沟通响应) 接口:新增/查询/上传资质/评分接口

  • 采购申请(PR)

来源:销售单/工单/手动 支持物料需求合并、优先级与到货期

自动生成建议供应商(基于历史价格/供应商能力) 审批链:支持多级审批、金额阈值、并行审批

  • 采购订单(PO)

PO 版本控制(变更单) 支持部分到货、暂扣(质检未通过) 支持价格、到货分批、交货地点

支持 PO 与供应商回签

  • 采购入库 / 验收

收货时支持批次/序列号、质检计划、不良记录 与 WMS 联动:收货上架或直接发往生产线(JIT)

触发库存事务:可配置为延迟更新或即时事务

  • 采购退货

支持退货单、退货审批、费用分担、运费处理 与供应商结算 / 折扣 / 补料

  • 统计与报表

采购需求统计:按物料/工厂/项目汇总需求(支持导出)

PO 统计:按周期/供应商/物料 成交率/到货率/逾期率 看板:未交明细、逾期 PO、到货预测

  • 采购执行跟踪看板

实时数据:未交数量、逾期 PO、预期7天到货、供应商热力图

支持钻取:点某个供应商查看其 PO 列表、未交明细、历史绩效


七、开发技巧与工程实践

  • 事务与补偿:收货环节一定要在数据库事务中完成库存与收货单的写入;如果跨服务(财务/MES/WMS)则采用 SAGA 补偿或两段提交(慎用)。

建议:使用事件驱动(MQ)+幂等消费者。

  • 并发与幂等:PO 下发/供应商回签接口需要幂等设计(使用幂等 key、乐观锁)。PR 转 PO 时避免重复生成(加唯一约束或分布式锁)。
  • 审计与回溯:对关键操作(创建/变更/删除)写 Audit Log,并保存变更快照(JSON),便于稽核与追溯。
  • 批量操作:采购需求统计等操作做分页/分批,避免一次性查询过大数据集。对冷数据做归档策略。
  • 接口设计:RESTful + 分页 + 过滤条件;关键场景暴露批量 CSV 导入(供应商/RFQ/价格表)。
  • 可配置规则引擎:审批规则、合并规则、供应商选择规则应可配置,避免硬编码。
  • 错误恢复:建立补单/人工干预台(Operator Console),处理 MQ 失败或接口回调异常。
  • 测试:端到端测试要覆盖 PR→PO→收货→退货全链路,使用测试数据库与模拟供应商回调。
  • 安全与权限:基于角色的访问控制(RBAC),敏感操作(价格变更、PO 取消)需额外审批。
  • 性能:统计类查询使用物化视图或定时聚合任务,实时看板用缓存(Redis)+增量更新。

八、参考实现

下面给出一个可直接跑的后端参考,采用 FastAPI + SQLAlchemy(SQLite)展示关键 API。

代码语言:txt
复制
# procurement_app.py
from fastapi import FastAPI, HTTPException, WebSocket, Depends
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from typing import List, Optional
from datetime import datetime, date
from sqlalchemy import (
    create_engine, Column, Integer, String, DateTime, ForeignKey,
    Float, Enum, Boolean, Date
)
from sqlalchemy.orm import sessionmaker, declarative_base, relationship, Session
from sqlalchemy.exc import IntegrityError
import enum
import uuid
# DB setup (SQLite for demo)
SQLALCHEMY_DATABASE_URL = "sqlite:///./procurement_demo.db"
engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(bind=engine, autocommit=False, autoflush=False)
Base = declarative_base()
# Enums
class PRStatus(str, enum.Enum):
    DRAFT = "DRAFT"
    SUBMITTED = "SUBMITTED"
    APPROVED = "APPROVED"
    REJECTED = "REJECTED"
class POStatus(str, enum.Enum):
    DRAFT = "DRAFT"
    SENT = "SENT"
    PART_RECEIVED = "PART_RECEIVED"
    RECEIVED = "RECEIVED"
    CANCELLED = "CANCELLED"
# Models
class Supplier(Base):
    __tablename__ = "suppliers"
    id = Column(Integer, primary_key=True, index=True)
    code = Column(String, unique=True, index=True)
    name = Column(String, index=True)
    contact = Column(String, nullable=True)
    lead_time_mean = Column(Integer, default=7)  # days
    rating = Column(Float, default=5.0)
class Material(Base):
    __tablename__ = "materials"
    id = Column(Integer, primary_key=True, index=True)
    sku = Column(String, unique=True)
    description = Column(String)
class PurchaseRequest(Base):
    __tablename__ = "purchase_requests"
    id = Column(Integer, primary_key=True, index=True)
    pr_no = Column(String, unique=True, default=lambda: "PR-" + uuid.uuid4().hex[:8])
    requester = Column(String)
    created_at = Column(DateTime, default=datetime.utcnow)
    status = Column(Enum(PRStatus), default=PRStatus.DRAFT)
    lines = relationship("PurchaseRequestLine", back_populates="pr")
class PurchaseRequestLine(Base):
    __tablename__ = "pr_lines"
    id = Column(Integer, primary_key=True, index=True)
    pr_id = Column(Integer, ForeignKey("purchase_requests.id"))
    material_id = Column(Integer, ForeignKey("materials.id"))
    qty = Column(Float)
    required_date = Column(Date)
    priority = Column(Integer, default=3)
    pr = relationship("PurchaseRequest", back_populates="lines")
    material = relationship("Material")
class PurchaseOrder(Base):
    __tablename__ = "purchase_orders"
    id = Column(Integer, primary_key=True, index=True)
    po_no = Column(String, unique=True, default=lambda: "PO-" + uuid.uuid4().hex[:8])
    supplier_id = Column(Integer, ForeignKey("suppliers.id"))
    created_by = Column(String)
    status = Column(Enum(POStatus), default=POStatus.DRAFT)
    created_at = Column(DateTime, default=datetime.utcnow)
    lines = relationship("PurchaseOrderLine", back_populates="po")
    supplier = relationship("Supplier")
class PurchaseOrderLine(Base):
    __tablename__ = "po_lines"
    id = Column(Integer, primary_key=True, index=True)
    po_id = Column(Integer, ForeignKey("purchase_orders.id"))
    material_id = Column(Integer, ForeignKey("materials.id"))
    qty_ordered = Column(Float)
    qty_received = Column(Float, default=0.0)
    unit_price = Column(Float, default=0.0)
    promised_date = Column(Date)
    po = relationship("PurchaseOrder", back_populates="lines")
    material = relationship("Material")
class Receipt(Base):
    __tablename__ = "receipts"
    id = Column(Integer, primary_key=True, index=True)
    receipt_no = Column(String, unique=True, default=lambda: "RC-" + uuid.uuid4().hex[:8])
    po_id = Column(Integer, ForeignKey("purchase_orders.id"))
    created_at = Column(DateTime, default=datetime.utcnow)
    warehouse = Column(String, default="MAIN")
    lines = Column(String)  # for demo: JSON string of received lines
Base.metadata.create_all(bind=engine)
# Pydantic schemas
class PRLineIn(BaseModel):
    material_sku: str
    qty: float
    required_date: date
    priority: int = 3
class PRIn(BaseModel):
    requester: str
    lines: List[PRLineIn]
class POCreateIn(BaseModel):
    supplier_code: str
    created_by: str
    items: List[dict]  # each: {material_sku, qty, unit_price, promised_date}
# FastAPI app
app = FastAPI(title="Procurement Demo API (MTO)")
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)
# Dependency
def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()
# Helper: get or create material
def get_or_create_material(db: Session, sku: str, description: Optional[str] = None):
    m = db.query(Material).filter(Material.sku == sku).first()
    if m:
        return m
    m = Material(sku=sku, description=description or sku)
    db.add(m)
    db.commit()
    db.refresh(m)
    return m
# API: create PR
@app.post("/pr", summary="创建采购申请 PR")
def create_pr(pr_in: PRIn, db: Session = Depends(get_db)):
    pr = PurchaseRequest(requester=pr_in.requester)
    db.add(pr)
    db.commit()
    db.refresh(pr)
    for l in pr_in.lines:
        mat = get_or_create_material(db, l.material_sku)
        pr_line = PurchaseRequestLine(pr_id=pr.id, material_id=mat.id, qty=l.qty,
                                      required_date=l.required_date, priority=l.priority)
        db.add(pr_line)
    db.commit()
    db.refresh(pr)
    return {"pr_no": pr.pr_no, "id": pr.id, "status": pr.status}
# API: submit PR (simple approval simulation)
@app.post("/pr/{pr_id}/submit", summary="提交 PR 审批")
def submit_pr(pr_id: int, db: Session = Depends(get_db)):
    pr = db.query(PurchaseRequest).filter(PurchaseRequest.id == pr_id).first()
    if not pr:
        raise HTTPException(status_code=404, detail="PR not found")
    pr.status = PRStatus.SUBMITTED
    db.commit()
    return {"pr_no": pr.pr_no, "status": pr.status}
# API: approve PR and create PO (very simplified logic: one PO per PR, using supplier heuristic)
@app.post("/pr/{pr_id}/approve", summary="审批 PR 并生成 PO(示例)")
def approve_pr_and_create_po(pr_id: int, supplier_code: str, db: Session = Depends(get_db)):
    pr = db.query(PurchaseRequest).filter(PurchaseRequest.id == pr_id).first()
    if not pr:
        raise HTTPException(status_code=404, detail="PR not found")
    supplier = db.query(Supplier).filter(Supplier.code == supplier_code).first()
    if not supplier:
        raise HTTPException(status_code=404, detail="Supplier not found")
    # mark PR approved
    pr.status = PRStatus.APPROVED
    db.commit()
    # create PO
    po = PurchaseOrder(supplier_id=supplier.id, created_by="system-batch", status=POStatus.SENT)
    db.add(po); db.commit(); db.refresh(po)
    for line in pr.lines:
        pol = PurchaseOrderLine(po_id=po.id, material_id=line.material_id,
                                qty_ordered=line.qty, qty_received=0.0,
                                unit_price=0.0, promised_date=line.required_date)
        db.add(pol)
    db.commit()
    return {"po_no": po.po_no, "po_id": po.id}
# API: manually create PO
@app.post("/po", summary="创建 PO(手工)")
def create_po(payload: POCreateIn, db: Session = Depends(get_db)):
    supplier = db.query(Supplier).filter(Supplier.code == payload.supplier_code).first()
    if not supplier:
        raise HTTPException(status_code=404, detail="Supplier not found")
    po = PurchaseOrder(supplier_id=supplier.id, created_by=payload.created_by, status=POStatus.SENT)
    db.add(po); db.commit(); db.refresh(po)
    for it in payload.items:
        mat = get_or_create_material(db, it["material_sku"])
        pol = PurchaseOrderLine(po_id=po.id, material_id=mat.id, qty_ordered=it["qty"],
                                unit_price=it.get("unit_price", 0.0), promised_date=it.get("promised_date"))
        db.add(pol)
    db.commit()
    return {"po_no": po.po_no, "po_id": po.id}
# API: receive goods (partial allowed)
@app.post("/po/{po_id}/receive", summary="入库/收货(简化)")
def receive_po(po_id: int, lines: List[dict], warehouse: str = "MAIN", db: Session = Depends(get_db)):
    po = db.query(PurchaseOrder).filter(PurchaseOrder.id == po_id).first()
    if not po:
        raise HTTPException(status_code=404, detail="PO not found")
    # update quantities
    for l in lines:
        pol = db.query(PurchaseOrderLine).filter(PurchaseOrderLine.po_id == po_id,
                                                 PurchaseOrderLine.material_id == db.query(Material).filter(Material.sku == l["material_sku"]).first().id).first()
        if not pol:
            raise HTTPException(status_code=400, detail=f"PO line for {l['material_sku']} not found")
        pol.qty_received = (pol.qty_received or 0) + l["qty"]
    # set status
    all_received = all([ln.qty_received >= ln.qty_ordered for ln in po.lines])
    po.status = POStatus.RECEIVED if all_received else POStatus.PART_RECEIVED
    db.add(po)
    # create receipt (simple)
    r = Receipt(po_id=po.id, warehouse=warehouse, lines=str(lines))
    db.add(r)
    db.commit()
    return {"po_no": po.po_no, "status": po.status}
# API: create supplier (demo)
@app.post("/supplier", summary="新增供应商")
def create_supplier(code: str, name: str, db: Session = Depends(get_db)):
    s = Supplier(code=code, name=name)
    db.add(s)
    try:
        db.commit()
    except IntegrityError:
        db.rollback(); raise HTTPException(status_code=400, detail="Supplier code already exists")
    db.refresh(s)
    return {"supplier_id": s.id, "code": s.code}
# API: stats: get pending/overdue POs
@app.get("/stats/po_summary", summary="采购统计")
def po_summary(db: Session = Depends(get_db)):
    total_po = db.query(PurchaseOrder).count()
    pending_po = db.query(PurchaseOrder).filter(PurchaseOrder.status != POStatus.RECEIVED).count()
    # overdue: promised_date < today and qty_received < qty_ordered
    from sqlalchemy import func
    sub = db.query(PurchaseOrderLine).filter(PurchaseOrderLine.qty_received < PurchaseOrderLine.qty_ordered,
                                             PurchaseOrderLine.promised_date < date.today()).count()
    return {"total_po": total_po, "pending_po": pending_po, "overdue_lines": sub}
# WebSocket for simple tracking board (push minimal updates)
connected = []
@app.websocket("/ws/board")
async def ws_board(ws: WebSocket):
    await ws.accept()
    connected.append(ws)
    try:
        while True:
            # ping/pong or wait for client messages; in demo simply echo
            data = await ws.receive_text()
            await ws.send_text(f"echo: {data}")
    except Exception:
        connected.remove(ws)

说明与扩展建议

  • 上面示例演示 PR 创建、提交、审批(简化)、PO 生成、收货与统计。实际系统需补充:鉴权、审批流引擎、邮件/通知、并发控制、幂等处理、MQ 异步事件、文件/资质管理、价格历史、采购条款管理、接口限流、审计日志等。
  • 对入库处理应与 WMS 做事务式集成,或通过消息总线通知 WMS 执行上架。
  • 看板方面可让后端定期计算关键指标并缓存(Redis),并通过 WebSocket 推送增量更新。

九、前端看板实现建议

需求要点

  • 实时性:显示未交货明细、逾期清单、7 天内到货预测、供应商绩效排名
  • 交互:能按供应商/物料筛选、钻取到 PO 明细、导出 Excel
  • 视觉:采用红橙绿标识逾期风险、折线/柱状图表示到货趋势、表格展示明细

技术实现建议

  • 前端框架:React + Ant Design(企业级),看板使用图表库(Recharts 或 ECharts)
  • 数据源:REST 接口 + WebSocket(用于推送实时变更或进度)
  • 性能:看板定时拉取汇总缓存 + WebSocket 增量通知;明细表格使用服务端分页
  • 用户体验:支持关注/订阅某个 PO 或供应商(消息推送和邮件),并提供导出功能

示意交互

  • 主界面:顶部 KPI 条(未交 PO 数、逾期行数、到货率)、左侧供应商排名、右侧到货预测图、下方明细表
  • 点击某条未交记录 → 右侧弹窗显示 PO 历史、回签文件、收货记录与质检报告

十、实施效果与 KPI

短期(上线 1~3 个月)

  • PO 到发出时间缩短 X%(目标 30%)
  • PR → PO 周转时间缩短(从提交到下发)
  • 缺料导致的生产停线次数下降

中期(3~9 个月)

  • 到货及时率(OTD,On Time Delivery)提升(目标 ≥95%)
  • 供应商绩效透明化,替换低绩效供应商比例下降
  • 库存周转率提升(降低物料占用)

长期

  • 采购成本下降(通过集中议价/供应商管理)
  • 采购预测准确率提高(MRP 与 PR 更精确协同)
  • 供应商协同自动化(供应商门户+ EDI)

结论

做好 MTO 场景下的采购管理,不只是一个模块的开发,而是把采购、生产、库存与供应商紧密联动,形成可追溯、可度量的闭环。技术上推荐微服务化、事件驱动、审计与补偿机制并重;

产品上要把审批/合并/供应商选择规则做到可配置化,减少对代码的频繁改动。


常见问题

FAQ 1:MTO 场景中 PR 如何自动转 PO?什么时候应该自动合并 PR?

在 MTO 场景下,PR 转 PO 的决策需要平衡响应速度与成本。一般有两种策略:

  • 立即转单(快速响应单个订单)或延迟合并(等待短时间窗口,把相近交期/相同供应商的多个 PR 合并成一个 PO 以节省运输与议价成本)。

建议做成可配置规则:若 PR 优先级高或交期短于供应商平均交期阈值,则立即生成 PO;否则进入一个短窗口(例如 12–24 小时)做合并。

  • 同时规则里要考虑生产批次、退换货风险与仓储可用性。

实现时可使用“合并引擎”:把待处理 PR 放入队列或缓存,按供应商/物料/交期分桶,按规则触发合并或立即下发;并做好幂等和分布式锁,防止重复下单。

FAQ 2:采购入库时如何处理质量检验和批次/序列号追溯?

质量检验在 MTO 特别重要:收货时应先做初检并记录判定(合格/待检/不合格)。

系统应支持:按 PO Line 创建检验计划、记录检验结果、生成批次/序列号并与物料关联。若质检不合格,自动触发退货或返修工单,并把该批次标记为隔离(Quarantine)。

  • 实现建议:收货事务先写“收货记录(未上账)”,通知质检系统或质检人员;
  • 质检通过后再执行库存入库(触发 WMS 上架或将库存标为可用)。

所有操作都要写审计日志,批次/序列号信息必须可追溯到 PO、供应商与源订单。

FAQ 3:如何保证 PO 变更(如价格/数量/交期)不会影响财务和生产?

PO 变更是采购实施中常见且风险较高的操作。最佳实践包括:

所有变更必须有变更单(PO Revision),并记录变更原因、变更人、时间、审批人和变更前后对比。

系统要保证变更与发票、入库、在制品(WIP)和计划的联动:

例如,若在 PO 已部分收货后变更价格,需判断是否影响已到货物料的成本分摊;

若变更交期,应触发对下游生产计划(MES/MRP)的通知。

实现上,建议使用版本号(po.version)并在每次变更时生成一条新版本记录,保持旧版本可回溯;

并在接口层面做权限校验——例如超过某金额或关键字段(交期/数量)变更时需额外审批。

对于财务,变更需触发财务凭证调整流程(或给出待处理项),并由财务审核最终账务影响。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、到底什么是 ERP(离散制造-MTO)系统?
  • 二、主要功能
  • 三、架构图
  • 四、数据模型
  • 五、采购业务流程
  • 六、功能模块拆解
  • 七、开发技巧与工程实践
  • 八、参考实现
  • 九、前端看板实现建议
  • 十、实施效果与 KPI
  • 结论
  • 常见问题
    • FAQ 2:采购入库时如何处理质量检验和批次/序列号追溯?
    • FAQ 3:如何保证 PO 变更(如价格/数量/交期)不会影响财务和生产?
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档