3. 原料入库批次信息采集与关联

入库批次采集的对象与关联关系

原料入库时,需要把四类信息绑定到同一个入库批次记录上:批次号(追溯主键)、供应商(来源责任方)、质检报告(合规凭证)、入库单(数量与时间凭证)。四者通过入库批次记录聚合,任一缺失都会造成追溯断链。下图展示采集字段与关联方向。

入库批次记录:四类信息的聚合与关联 入库批次记录 batch_no(唯一) material_id + qty + in_time 批次号 batch_no 格式:原料码-供应商码-日期-序号 约束:全局唯一、可解析 供应商 supplier_id 名称 / 许可证号 / 联系人 外键 → 供应商主表 质检报告 qc_report_no 报告编号 / 结论 / 检验日期 约束:结论须为合格 入库单 inbound_no 单号 / 数量 / 入库时间 约束:数量与批次一致 1:1 N:1 1:1 N:1

采集表结构与约束

用一张入库批次表承载关联,关键约束决定数据一致性:批次号唯一、质检结论必须合格、入库数量与批次数量一致。以下建表语句可直接在 SQLite 中执行验证。

CREATE TABLE supplier (
    supplier_id   TEXT PRIMARY KEY,
    supplier_name TEXT NOT NULL,
    license_no    TEXT NOT NULL
);

CREATE TABLE inbound_batch (
    batch_no      TEXT PRIMARY KEY,          -- 批次号,全局唯一
    material_id   TEXT NOT NULL,             -- 原料编码
    supplier_id   TEXT NOT NULL,
    qc_report_no  TEXT NOT NULL,
    qc_result     TEXT NOT NULL CHECK (qc_result IN ('合格','不合格')),
    inbound_no    TEXT NOT NULL,             -- 入库单号
    qty           REAL NOT NULL CHECK (qty > 0),
    in_time       TEXT NOT NULL,
    FOREIGN KEY (supplier_id) REFERENCES supplier(supplier_id)
);

CREATE INDEX idx_batch_material ON inbound_batch(material_id);
CREATE INDEX idx_batch_inbound  ON inbound_batch(inbound_no);

关联录入与一致性校验

录入时先校验批次号格式与唯一性,再校验质检结论,最后写入并回查。下面用 Python 标准库 sqlite3 完成一次完整录入与多批次一致性检查。

import sqlite3, re

def parse_batch_no(batch_no):
    # 原料码-供应商码-日期(YYYYMMDD)-序号
    m = re.fullmatch(r"([A-Z]{2}\d{3})-([A-Z]{3})-(\d{8})-(\d{3})", batch_no)
    if not m:
        raise ValueError(f"批次号格式非法: {batch_no}")
    return m.groups()

def insert_batch(conn, rec):
    parse_batch_no(rec["batch_no"])
    if rec["qc_result"] != "合格":
        raise ValueError("质检不合格,禁止入库")
    cur = conn.execute("SELECT 1 FROM inbound_batch WHERE batch_no=?", (rec["batch_no"],))
    if cur.fetchone():
        raise ValueError(f"批次号重复: {rec['batch_no']}")
    conn.execute(
        "INSERT INTO inbound_batch VALUES (?,?,?,?,?,?,?,?)",
        (rec["batch_no"], rec["material_id"], rec["supplier_id"],
         rec["qc_report_no"], rec["qc_result"], rec["inbound_no"],
         rec["qty"], rec["in_time"]))
    conn.commit()

def check_consistency(conn, material_id):
    rows = conn.execute(
        "SELECT batch_no, supplier_id, qty, inbound_no FROM inbound_batch WHERE material_id=?",
        (material_id,)).fetchall()
    batch_nos = [r[0] for r in rows]
    assert len(batch_nos) == len(set(batch_nos)), "存在重复批次号"
    for r in rows:
        assert r[2] > 0, f"{r[0]} 数量异常"
        assert r[3], f"{r[0]} 缺少入库单号"
    return rows

conn = sqlite3.connect(":memory:")
conn.executescript(open("schema.sql").read())
conn.execute("INSERT INTO supplier VALUES ('SUP01','晨光乳业原料部','SC123456')")

insert_batch(conn, {"batch_no":"RM001-SUP-20240510-001","material_id":"RM001",
    "supplier_id":"SUP01","qc_report_no":"QC20240510-01","qc_result":"合格",
    "inbound_no":"IN20240510-01","qty":500.0,"in_time":"2024-05-10 09:20"})
insert_batch(conn, {"batch_no":"RM001-SUP-20240512-002","material_id":"RM001",
    "supplier_id":"SUP01","qc_report_no":"QC20240512-03","qc_result":"合格",
    "inbound_no":"IN20240512-02","qty":300.0,"in_time":"2024-05-12 14:05"})

rows = check_consistency(conn, "RM001")
print("批次数量:", len(rows))
for r in rows:
    print(r)

预期输出为两条记录,批次号分别为 RM001-SUP-20240510-001 与 RM001-SUP-20240512-002,数量 500 与 300,均带入库单号。若把第二条的批次号改成与第一条相同,insert_batch 会抛出“批次号重复”;若把 qc_result 改为“不合格”,则抛出“质检不合格,禁止入库”。

同一原料多批次入库时,一致性检查应至少覆盖三点:批次号不重复、每批数量为正、每批都能回指到唯一入库单。这三项通过,才能保证后续投料绑定与正反向追溯有据可查。
批次号中的供应商码必须与 supplier_id 对应的供应商一致,否则会出现“编码指向 A 供应商、外键指向 B 供应商”的隐性错配。建议在录入函数中增加一步:解析出的供应商码与 supplier_id 映射比对,不一致即拒绝写入。